前端笔记

317 阅读10分钟

node

  • 路径问题
__dirname:    获得当前执行文件所在目录的完整目录名
__filename:   获得当前执行文件的带有完整绝对路径的文件名
process.cwd():获得当前执行node命令时候的文件夹目录名 
./:           文件所在目录
具体讲解文章:https://github.com/jawil/blog/issues/18
  • promisify
将一个接收回调函数参数的函数转换成一个返回Promise的函数。
  • glob.sync() 获取文件路径

  • require.context
<!--一个webpack的api,通过执行require.context函数获取一个特定的上下文,主要用来实现自动化导入模块,在前端工程中,如果遇到从一个文件夹引入很多模块的情况,可以使用这个api,它会遍历文件夹中的指定文件,然后自动导入,使得不需要每次显式的调用import导入模块-->

require.context函数接受三个参数

directory {String} -读取文件的路径

useSubdirectories {Boolean} -是否遍历文件的子目录

regExp {RegExp} -匹配文件的正则
语法: require.context(directory, useSubdirectories = false, regExp = /^.//);


值得注意的是require.context函数执行后返回的是一个函数,并且这个函数有3个属性

1 resolve {Function} -接受一个参数request,request为test文件夹下面匹配文件的相对路径,返回这个匹配文件相对于整个工程的相对路径

2 keys {Function} -返回匹配成功模块的名字组成的数组

3 id {String} -执行环境的id,返回的是一个字符串,主要用在module.hot.accept,应该是热加载?

require.context


  • process.stdin 数据流

  • path.resolve() 绝对地址拼接
var path = require("path")     //引入node的path模块

path.resolve('/foo/bar', './baz')   // returns '/foo/bar/baz'
path.resolve('/foo/bar', 'baz')   // returns '/foo/bar/baz'
path.resolve('/foo/bar', '/baz')   // returns '/baz'
path.resolve('/foo/bar', '../baz')   // returns '/foo/baz'
path.resolve('home','/foo/bar', '../baz')   // returns '/foo/baz'
path.resolve('home','./foo/bar', '../baz')   // returns '/home/foo/baz'
path.resolve('home','foo/bar', '../baz')   // returns '/home/foo/baz'

vue-cli3-project项目中自动生成文件脚本中
 const componentDirectory = resolve('../src/components', inputName)
 componentDirectory就是:
 D:\exitMongoDb\project\gitProject\git-vue-cli3\vue-cli3-project\src\components\list 
  • 文件写入
const generateFile = (path, data) => {
  if (fs.existsSync(path)) {
    errorLog(`${path}文件已存在`)
    return
  }
  return new Promise((resolve, reject) => {
    fs.writeFile(path, data, 'utf8', err => {
      if (err) {
        errorLog(err.message)
        reject(err)
      } else {
        resolve(true)
      }
    })
  })
}
调用使用 await 
await generateFile(componentVueName, vueTemplate(componentName))

vue

  • 声明周期
beforeCreate() 在实例创建之间执行,数据未加载状态
created() 在实例创建、数据加载后,能初始化数据,dom渲染之前执行
beforeMount() 虚拟dom已创建完成,在数据渲染前最后一次更改数据
mounted() 页面、数据渲染完成,真实dom挂载完成
beforeUpadate() 重新渲染之前触发
updated() 数据已经更改完成,dom 也重新 render 完成,更改数据会陷入死循环
beforeDestory() 和 destoryed() 前者是销毁前执行(实例仍然完全可用),后者则是销毁后执行

  • Vue.nextTick()
简单理解,等待最近的一次dom完成渲染后会执行的方法

  • 全局组件
1:components 中新建文件夹
例如:
    loading
        index.js
        index.vue
    
        
  • index.vue:组件的具体内容,只有一个参数isShow

<template>
  <transition name="fade">
    <!--loding s-->
    <div v-if="isShow" ontouchmove="return false;" class="loadingbg">
      <div class="loading">
        <div class="loading-rotate lazy"></div>
        <div class="loading_text">努力加载中</div>
      </div>
    </div>
  </transition>
</template>

<script>
export default {
  data() {
    return {
      isShow: false
    };
  }
};
</script>

<style lang="less" scoped>
.loading-rotate {
  margin: 0 auto;
  position: relative;
  width: 30px;
  height: 30px;
  -webkit-animation: rotate 1s linear infinite;
  animation: rotate linear 1s infinite;
  transform-origin: center center;
  box-sizing: border-box;
}
.loading-rotate:before {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  box-sizing: border-box;
  border: 3px solid transparent;
  border-top-color: #ddd;
  border-radius: 50%;
  z-index: 10;
}
.loading-rotate:after {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  width: 30px;
  height: 30px;
  border: 3px solid #f2f4f7;
  box-sizing: border-box;
  border-radius: 50%;
}
/*旋转位置不停变化*/
.loading-rotate.lazy {
  -webkit-animation: rotate-lazy 6s ease-in-out infinite;
  animation: rotate-lazy ease-in-out 6s infinite;
}
@-webkit-keyframes rotate-lazy {
  0 {
    -webkit-transform: rotate(0);
  }
  25% {
    -webkit-transform: rotate(450deg);
  }
  50% {
    -webkit-transform: rotate(900deg);
  }
  75% {
    -webkit-transform: rotate(1350deg);
  }
  100% {
    -webkit-transform: rotate(1800deg);
  }
}

.loadingbg {
  background-color: #f2f4f7;
  width: 100%;
  height: 100%;
  position: fixed;
  top: 0;
  left: 0;
  z-index: 998;
}
.loadingbg .loading {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 71px;
  height: 71px;
  margin: -33px 0 0 -33px;
}
.loadingbg .loading .loading_icon {
  display: block;
  width: 36px;
  height: 36px;
  background: url(//appnew.ly.com/hc/1/img/icon_tg_loading1.gif) no-repeat;
  background-size: 36px 36px;
  margin: 0 auto;
}
.loadingbg .loading .loading_text {
  font-size: 14px;
  margin-top: 12px;
  text-align: center;
  color: #999;
  font-family: "Microsoft YaHei", "微软雅黑", Arial, SimSun, sans-serif;
}
</style>
  • index.js:主要进行全局组件挂载
import loading from "./index.vue"
export default {
    install :vue =>{
    <!--extend创建的是一个组件构造器,而不是一个具体的组件实例,还是要通过Vue.components注册才可以使用的-->
        const MyLoading = vue.extend(loading);
        <!--实例化组件-->
        const instance = new MyLoading();
        <!--$mount方法是用来挂载我们的Vue.extend扩展的-->
        instance.$mount(document.createElement('div'));
        document.body.appendChild(instance.$el);
        <!--挂载到vue实例上,instance可以拿到组件中data的值,进行赋值-->
        vue.prototype.$loading=(bool)=>{
            instance.isShow = bool;
        }
    }
}
main.js中 将组件装到vue中
import loading from './components/Loading';
Vue.use(loading)

  • 组件的一种使用方法

1.像普通引用子组件一样将组件引进来

import Confirm from "@/components/confirmToast.vue";

2.想普通组件一样在父组件中注册

components: {
    Confirm
  },

3.不同的地方来了,在template中使用

<Confirm ref="confirmToast" @userControl="toastType"></Confirm>

4.注意ref="confirmToast", 子组件中有一个show(参数1,参数2)方法 在方法中使用show方法的时候可以直接这样调用,不需要通过prop传参,直接调用子组件中的方法,子组件的其他方法也这样用:

this.$refs.confirmToast.show(content, netConfig);

  • 全局过滤器
  1. src下新建filter文件夹中新建index.js
<!--具体的过滤器方法-->
export const yuan = value =>
  isNumber(value) ? `¥${(value / 100).toFixed(2)}` : value;
<!--挂载方法在vue全局过滤器中-->
export default {
  install(Vue) {
    Vue.filter('yuan', yuan);
  }
};
  1. main.js中使用方法
import filters from '@/filter';
Vue.use(filters);

  • 方法的使用,作用:保存组件的状态,回退后组件不刷新
  1. app.vue中
<keep-alive>
    <router-view :key="$route.fullPath" v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive"></router-view>
  1. :key="$route.fullPath"的作用是页面跳转当前页面地址的时候保证页面刷新
  2. $route.meta.keepAlive区分当前地址是不是需要保存组件状态
  3. 在router中进行设置keepAlive的值,meta中设置为true后当前地址就会保存组件状态了 列表页就会保存组件状态,详情页不会
const routes = [{
    path: "/",
    name: "readBuyList",
    meta: {
      title: "边看边买",
      keepAlive: true
    },
    components: {
      default: readBuyList,
      tabbar: Tabbar
    },
  },
  {
    path: "/readBuyDetail",
    name: "readBuyDetail",
    meta: {
      title: "边看边买"
    },
    component: readBuyDetail
  }
];

  • axios vue项目封装
  1. src下面新建api文件夹
  • apiList.js 所有的接口目录
  • axios.js 具体axios方法的封装
  • baseUrl.js 区分不同服务器接口地址的目录,用于区分dev,qa,production环境地址
  • index.js 具体封装axios get post delete等方法
  • install.js 将封装的方法挂载在vue实例上(用于main.js vue.use()使用)

边看边买项目自己封装的



  • vue-axios取消请求,拦截器设置 link
const CancelToken = axios.CancelToken
const httpPending = [] // 用于存储每个ajax请求的取消函数和ajax标识

// 取消请求方法
const cancelHttp = (name, config = {}) => {
    httpPending.forEach((e, i) => {
        if (e.n === name || e.n === config.xhrName) { // 当前请求在数组中存在时执行函数体
            e.f() // 执行取消操作
            httpPending.splice(i, 1) // 把这条记录从数组中移除
        }
    })
}
// 请求拦截器
axios.interceptors.request.use(config => {
    // 取消上一次未完成的相同请求,注意项目中是否存在风险
    cancelHttp(null, config)

    config.cancelToken = new CancelToken(c => {
        if (config.xhrName) {
            httpPending.push({
                n: config.xhrName,
                u: `${config.url}&${config.method}`,
                f: c
            })
        }
    })

    return config
}, error => Promise.reject(error))

// 响应拦截器
axios.interceptors.response.use(res => {
    cancelHttp(null, res.config) // 响应成功把已经完成的请求从 httpPending 中移除

    checkStatus(res) // 校验响应状态
    const response = res.data

    return Promise.resolve(response)
}, error => Promise.reject(error))


  • $refs
// 父组件
<home ref="home"/>

mounted(){
  console.log(this.$refs.home) //即可拿到子组件的实例,就可以直接操作 data 和 methods
}
  • 下拉刷新组件
<div
  class="yo-scroll"
  :class="{'down':(refresState===0),'up':(refresState==1),refresh:(refresState===2),touch:touching}"
  @touchstart="touchStart($event)"
  @touchmove="touchMove($event)"
  @touchend="touchEnd($event)"
>
  <div class="inner" :style="{ transform: 'translate3d(0, ' + top + 'px, 0)' }">
    <div class="pull-refresh">
        <span class="down-tip">下拉更新</span>
        <span class="up-tip">松开更新</span>
        <span class="refresh-tip">更新中</span>
    </div>
    //插槽用于显示滚动内容
    <slot> </slot>
  </div>
</div>
  
<script>
export default {
  props: {
    offset: {
      type: Number,
      default: 40
    },
    enableRefresh: {
      type: Boolean,
      default: true
    },
    onRefresh: {
      type: Function,
      default: undefined,
      required: false
    }
  },
  data() {
    return {
      top: 0,
      refresState: 0,
      startY: 0,
      touching: false
    };
  },
  methods: {
    touchStart(e) {
      // 获取滚动开始时点击点的y轴坐标
      this.startY = e.targetTouches[0].pageY;
      // 获取点击元素的滚轮在下拉时的位置
      this.startScroll = this.$el.scrollTop || 0;
      this.touching = true;
    },
    touchMove(e) {
      // 是否能滚动,点击元素的滚轮在下拉时的位置是否大于0,是不是已经开始滚动了,如果满足其中一个场景,不继续执行,保证要下拉的元素没有滚动条显示
      if (!this.enableRefresh || this.$el.scrollTop > 0 || !this.touching) {
        return;
      }
      // 计算点击的点在滚动时y轴的坐标 - 滚动开始时点击点的y轴坐标 - 开始滚动时点击的元素距离顶部的高度
      let diff = e.targetTouches[0].pageY - this.startY - this.startScroll;
      if (diff > 0) e.preventDefault();
      // 
      this.top =
        Math.pow(diff, 0.8) + (this.refresState === 2 ? this.offset : 0);

      if (this.refresState === 2) {
        return;
      }
      if (this.top >= this.offset) {
        this.refresState = 1;
      } else {
        this.refresState = 0;
      }
    },
    touchEnd(e) {
      if (!this.enableRefresh) return;
      this.touching = false;
      if (this.refresState === 2) {
        // 刷新中
        this.refresState = 2;
        this.top = this.offset;
        return;
      }
      if (this.top >= this.offset) {
        // 开始刷新
        this.refresh();
      } else {
        // 取消刷新
        this.refresState = 0;
        this.top = 0;
      }
    },
    refresh() {
      this.refresState = 2;
      this.top = this.offset;
      this.onRefresh(this.refreshDone);
    },
    refreshDone() {
      this.refresState = 0;
      this.top = 0;
    }
  }
};
</script>
<style>
.yo-scroll {
  position: absolute;
  top: 0px;
  right: 0;
  bottom: 0;
  left: 0;
  overflow: auto;
  -webkit-overflow-scrolling: touch;
  background-color: #ddd;
}
.yo-scroll .inner {
  position: absolute;
  top: -20px;
  width: 100%;
  transition-duration: 300ms;
}
.yo-scroll .pull-refresh {
  position: relative;
  left: 0;
  top: -10px;
  width: 100%;
  /* height: 20px; */
  display: flex;
  align-items: center;
  justify-content: center;
}
.yo-scroll.touch .inner {
  transition-duration: 0ms;
}
.yo-scroll.down .down-tip {
  display: block;
}
.yo-scroll.up .up-tip {
  display: block;
}
.yo-scroll.refresh .refresh-tip {
  display: block;
}
.yo-scroll .down-tip,
.yo-scroll .refresh-tip,
.yo-scroll .up-tip {
  display: none;
}
</style>

小程序

  • 一个计时器方法
var timer = null;
countDown() {
    let that = this;
    if(timer != null) {
        clearInterval(timer);
        timer = null;
    }
    timer = setInterval(function(){
        time --;
        that.setData({
            <!--更改时间变量-->
        })
        if(time<=0) {
            clearInterval(timer);
            
        }
    }, 1000);
}
  • 显示几行文字样式,超出...
white-space: pre-wrap;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;

第二种写法

word-break: break-all;
overflow:hidden; // 超出的文本隐藏
text-overflow:ellipsis; 
display:-webkit-box; // 将对象作为弹性伸缩盒子模型显示。
 /* autoprefixer: off */
 -webkit-box-orient:vertical;  //从上到下垂直排列子元素(设置伸缩盒子的子元素排列方式)
/* autoprefixer: on */ 
-webkit-line-clamp:2; // 结合上面两个属性,表示显示的行数。

  • 手机端滚动条滑动
-webkit-overflow-scrolling: touch;
  • 滚动事件监听
1:获取样式屏幕高度用于给scroll-view组件设置高度
wx.getSystemInfo({
      success: (result) => {
        that.setData({
          windowHeight: result.windowHeight
        })
      }
    });
2:主要思路,页面渲染完成之后,获取想要监听的元素距离顶部的高度:为了保证dom元素已经渲染完成
    1:使用计时器将wx.createSelectorQuery()包起来,
    2:在setData的回调中执行包含createSelectQuery的方法

 let selQuery = wx.createSelectorQuery();
        selQuery.select('#type' + index).boundingClientRect();
        selQuery.exec(res => {
          if (!res[0]) return;
          this.data.toTopList.push(res[0].top);
        })
3:通过scroll-view的bindscroll属性绑定一个方法,拿到滚轮距离顶部的实时高度,进行判断当前高度属于获取元素列表中哪一个区间里面,然后进行赋值操作

4:点击tab切换到对应位置,使用到的方法:scroll-view中的scroll-into-view属性,改属性是以id去获取要跳转的dom的,所以绑定在改属性中的值是不带#的id


遇到的问题:wx.createSelectorQuery()获取dom元素距离顶部高度的时候,
由于我获取的dom元素中含有不定高度的image元素,
并且image元素设置了小程序自带的 mode属性,
所以导致在获取dom元素距离顶部高度的时候不对,
一直认为是dom元素没有渲染完成就获取的原因,其实不是的,image 的mode属性会触发dom元素的二次渲染
为了解决这个问题,我把嵌套在image元素外面的view高度写死,
image还是使用mode属性就解决了

美美美发首页


npm

  • npm intall -S packjson文件dependencies生成依赖

>=es6

  • 数组去重
[...new Set([1,1])]
  • 手机号正则
function checkPhone(){
    if(!(/^1[345678]\d{9}$/.test(phone))){
        alert("手机号码有误,请重填");
        return false;
    }
}
  • 深度递归数组 Array.flat()
let multi = [1,2,3,[4,5,6,[7,8,9,[10,11,12]]]];
multi.flat();               // [1,2,3,4,5,6,Array(4)]
multi.flat().flat();        // [1,2,3,4,5,6,7,8,9,Array(3)]
multi.flat().flat().flat(); // [1,2,3,4,5,6,7,8,9,10,11,12]
multi.flat(Infinity);       // [1,2,3,4,5,6,7,8,9,10,11,12]

  • 深度递归数组 Array.flatMap
let array = [1, 2, 3, 4, 5];

console.log(array.map(x => [x, x * 2]))
[Array(2), Array(2), Array(2)]
0: (2)[1, 2]
1: (2)[2, 4]
2: (2)[3, 6]

<!--使用 flatMap 方式:-->
array.flatMap(v => [v, v * 2]);
[1, 2, 2, 4, 3, 6, 4, 8, 5, 10];

<!--多个数组统一序列号放在同一个数组中-->
let a =['a','b','c']
let b=['A','B',"C"]
let c = a.map((s,index)=> [s, b[index]])
console.log(c)
[Array(2), Array(2), Array(2)]
0: (2) ["a", "A"]
1: (2) ["b", "B"]
2: (2) ["c", "C"]

<!--使用flatMap将数组放在按序列号合并在一起-->
let d=a.flatMap((s,index)=>[s,b[index]])
console.log(d)
["a", "A", "b", "B", "c", "C"]
  • Object.fromEntries()
<!--与Object.entries(obj)相对,Object.entries(obj)是把对象转换 键和值一为一组的数组,而Object.fromEntries()则是把键值为一组的数组转化成对象-->
let obj = { apple : 10, orange : 20, banana : 30 };
let entries = Object.entries(obj);
entries;
(3) [Array(2), Array(2), Array(2)]
 0: (2) ["apple", 10]
 1: (2) ["orange", 20]
 2: (2) ["banana", 30]
let fromEntries = Object.fromEntries(entries);
{ apple: 10, orange: 20, banana: 30 }

  • 正则匹配字符串中符合条件值得数组
let string = "Hello";
let ret = string.match(/l/g); // (2) [“l”, “l”];
  • String.trimStart() 与 String.trimEnd() 删除字符串的开头空格 删除字符串的开头空格

  • es10支持try catch 方法中的catch的error参数不写

try {
    JSON.parse(text);
    return true;
}
catch
{
    return false;
}
  • 排序
var fruit = [
    { name: "Apple",      count: 13, },
    { name: "Pear",       count: 12, },
    { name: "Banana",     count: 12, },
    { name: "Strawberry", count: 11, },
    { name: "Cherry",     count: 11, },
    { name: "Blackberry", count: 10, },
    { name: "Pineapple",  count: 10, }
];
// 创建排序函数:
let my_sort = (a, b) => a.count - b.count;
// 执行稳定的ES10排序:
let sorted = fruit.sort(my_sort);
console.log(sorted);
  • 数组去重的方法:使用indexOf
  • 排序去重:
<!--去重-->
filter实现: filter() 不会改变原始数组
var array = [1,2,1,"1"];
function unique = function(arrary) {
    var res = array.filter(function(item,index,array) {
    return array.indexOf(item) === index;
        
    })
    return res;
}
console.log(unique(array));
<!--排序去重-->
var array = [4,1,2,1,"1"];
function unique(array) {
    return array.concat().sort().filter(function(item,index,array) {
        return !index || item !== array[index-1]
        
    })
}
<!--对象数组排序去重的-->
var array = [{a:1},{a:5},{a:1},{a:4}];
function unique(array) {
    var obj = {};
    return array.filter((item,index,array)=>{
        return Object.hasOwnProperty(typeof item + JSON.stringify(item)? false: (obj[typeof item + JSON.stringify(item)] =ture))
    })
    
}
<!--使用new Set()实现去重-->
var unique = (a) => [...new Set(a)]
  • 函数防抖
function debounce(fn, wait) {
  var timer = null;
  return function () {
      var context = this
      var args = arguments
      if (timer) {
          clearTimeout(timer);
          timer = null;
      }
      timer = setTimeout(function () {
          fn.apply(context, args)
      }, wait)
  }
}

var fn = function () {
  console.log('boom')
}

setInterval(debounce(fn,500),1000) // 第一次在1500ms后触发,之后每1000ms触发一次

setInterval(debounce(fn,2000),1000) // 不会触发一次(我把函数防抖看出技能读条,如果读条没完成就用技能,便会失败而且重新读条)

  • 函数节流
function throttle(fn, gapTime) {
  let _lastTime = null;

  return function () {
    let _nowTime = + new Date()
    if (_nowTime - _lastTime > gapTime || !_lastTime) {
      fn();
      _lastTime = _nowTime
    }
  }
}

let fn = ()=>{
  console.log('boom')
}

setInterval(throttle(fn,1000),10)

<!--另一种方法-->
// 函数节流
function throttle(method,context){
    clearTimeout(method.tId);
    method.tId = setTimeout(function(){
        method.call(context);
    }, 100)
}

  • 有条件的对象属性
const getUser = (emailIncluded) => {
  return {
    name: 'John',
    surname: 'Doe',
    ...emailIncluded && { email : 'john@doe.com' }
  }
}

const user = getUser(true);
console.log(user); // outputs { name: "John", surname: "Doe", email: "john@doe.com" }

const userWithoutEmail = getUser(false);
console.log(userWithoutEmail); // outputs { name: "John", surname: "Doe" }
  • 动态属性名
const dynamic = 'email';
let user = {
    name: 'John',
    [dynamic]: 'john@doe.com'
}
console.log(user); // outputs { name: "John", email: "john@doe.com" }
  • 判断对象是否为空
const isEmpty = (obj) => obj.keys().length !== 0;
  • 字符串转数组
console.log(Object.values('abc')); // => [ 'a', 'b', 'c' ]
console.log([...'abcd']);
  • 数组的知识点
push/pop 操作数组尾部,其实也可以说是栈相关的方法
shift/unshift 操作数组首部,他俩组合其实可以说将数组当成一个反过来的栈。
splice 唯一可以删除,插入元素的方法,当你要删除元素或者在哪个位置插入一个元素,找它准没错
slice 切片操作,或者说取原数组的连续子集,虽然不如 python 那样方便还可以传一个 step 参数,但是一般够用
indexOf/lastIndexOf /includes/find/findIndex 查找元素
join 拼接成字符串的,少数和字符串操作相关的方法之一
isArray 判断是否是个数组,面试经常问的一道非常非常非常基础的问题
of/from 构造或者转换数组的
reverse 翻转数组的,调用之后会改变原数组的函数不多,他也是一个
sort 其实可以和 reverse 放一类,reverse 叫倒序,sort 排序
keys/values/entries 和 Map 类似,返回的都是 Iterator
flat 数组扁平化
concat 拼接数组,因为效果是将数组参数打平后拼接,所以可以用来实现扁平化

  • forEach/filter/map/reduce/reduceRight/some/every 用于集合变换的
forEach 无返回值,常用于存粹的遍历,大多数情况可以替代 for 循环使用
filter 返回原数组的子集,用于对原数组的进行过滤筛选
map 返回的和原数组长度相同的映射数组,用于映射到另外一个数组
reduce/reduceRight 返回一个和初始值类型相同的值,中文叫归并操作,用于遍历数组每个元素收集需要的数据返回一个值
some/every 返回值都是 boolean 类型。只有 some 和 every 可以提前退出遍历,用于判断数组中有元素满足或者所有元素是否满足某条件。

filter/map 返回的都是新数组,并不会修改原数组,除非你在遍历过程中修改了原数组的元素,非常不建议在遍历操作中修改原数组,除非你清楚的知道修改的后果是什么
  • 数组方法的链式调用
const users = [
  { name: 'ly', level: 2 },
  { name: 'Bob', level: 1 },
  { name: 'Lily', level: 0 },
  { name: 'Tom', level: 3 },
];

const customerNames = users
  .filter(user => user.level !== 0)
  .map(customer => customer.name)
  .join(', ');

console.log(customerNames);// => ly, Bob, Tom
  • 查找数组中元素indexOf/lastIndexOf/includes/find/findIndex
indexOf:不能查找对象数组
lastIndexOf:indexOf的反向查找,不能查找对象数组
includes:用于判断数组中是否含有某个元素,返回布尔值
find:查找元素,返回元素本身,可以查找对象数组
findIndex :查找元素返回元素下表,可以查找对象数组

const arr = ['a', 'b', 'c', 'd'];

const isExists = arr.indexOf('k') != -1;
if (isExists) {
  console.log('数组中存在 k');
}
const handleSelect = newValue => {
  if (this.state.includes(newValue)) {
    message.error(`您已经选择过${newValue},不能重复选择!`);
  }
};


  • 类型检查
//安全类型检测
function isArray(value){
    return Object.prototype.toString.call(value) == "[object Array]";
}
// 注:在ie中在以COM对象形式实现的任何函数,isFunction()都将返回false
function isFunction(value){
    return Object.prototype.toString.call(value) == "[object Function]";
}

  • 拼接url参数
new URLSearchParams({ name: 'ly', age: 21 }).toString(); // => "name=ly&age=21"
  • 遍历对象
Object.entries(me).forEach(([key, value]) => console.log(`${key}: ${value}`));
  • 判断对象是否存在某一属性
const object = {
  prop: 'value'
};
if ('nonExistingProp' in object) {
  // ...
}
  • ...
1:合并对象时,相同属性会被替换,第一个里面不存在的属性直接加进去
var data1={a:1,b:2,c:{a:1,b:2}};
var data2={c:{c:1}};
var new1 = {...data1,...data2};
console.log(new1)
{a: 1, b: 2, c: {…}}

2:合并数组
var data1=[1,12,1];
var data2=[21];
var new1 = [...data1,...data2];
console.log(new1)
[1, 12, 1, 21]
  • 最大值
Math.max(...[1,2,3,4])
  • 求和
[1,2,3,4].arr.reduce(function (prev, cur) {
   return prev + cur;
 },0) //10 

css技巧

  • 不使用背景图的方式实现图片居中
<img class="image image-contain" src="https://picsum.photos/600/200" />
<img class="image image-cover" src="https://picsum.photos/600/200" />
.image {
  background: #34495e;
  border: 1px solid #34495e;
  width: 200px;
  height: 200px;
}
.image-contain {
  object-fit: contain;
  object-position: center;
}
.image-cover {
  object-fit: cover;
  object-position: right top;
}
  • 将元素垂直居中于另一个元素
<div class="ghost-trick">
  <div class="ghosting"><p>Vertically centered without changing the position property.</p></div>
</div>
.ghosting {
  height: 300px;
  background: #0ff;
}
.ghosting:before {
  content: '';
  display: inline-block;
  height: 100%;
  vertical-align: middle;
}
p {
  display: inline-block;
  vertical-align: middle;
}
  • 使用网格水平垂直居中子元素.
<div class="grid-centering"><div class="child">Centered content.</div></div>
.grid-centering {
  display: grid;
  justify-content: center;
  align-items: center;
  height: 100px;
}
  • 最后一个元素沾满高度
<div class="container">
  <div>Div 1</div>
  <div>Div 2</div>
  <div>Div 3</div>
</div>
html,
body {
  height: 100%;
  margin: 0;
}
.container {
  height: 100%;
  display: flex;
  flex-direction: column;
}
.container > div:last-child {
  background-color: tomato;
  flex: 1;
}

  • 使用transform居中子元素
<div class="parent"><div class="child">Centered content</div></div>
.parent {
  border: 1px solid #333;
  height: 250px;
  position: relative;
  width: 250px;
}
.child {
  left: 50%;
  position: absolute;
  top: 50%;
  transform: translate(-50%, -50%);
  text-align: center;
}

  • 多行文本截断显示
<p class="truncate-text-multiline">
  Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut
  labore et.
</p>

.truncate-text-multiline {
  overflow: hidden;
  display: block;
  height: 109.2px;
  margin: 0 auto;
  font-size: 26px;
  line-height: 1.4;
  width: 400px;
  position: relative;
}
.truncate-text-multiline:after {
  content: '';
  position: absolute;
  bottom: 0;
  right: 0;
  width: 150px;
  height: 36.4px;
  background: linear-gradient(to right, rgba(0, 0, 0, 0), #f5f6f9 50%);
}

  • 自定义滚动条
<div class="custom-scrollbar">
  <p>
    Lorem ipsum dolor sit amet consectetur adipisicing elit.<br />
    Iure id exercitationem nulla qui repellat laborum vitae, <br />
    molestias tempora velit natus. Quas, assumenda nisi. <br />
    Quisquam enim qui iure, consequatur velit sit?
  </p>
</div>
.custom-scrollbar {
  height: 70px;
  overflow-y: scroll;
}
/* To style the document scrollbar, remove `.custom-scrollbar` */
.custom-scrollbar::-webkit-scrollbar {
  width: 8px;
}
.custom-scrollbar::-webkit-scrollbar-track {
  box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
  border-radius: 10px;
}
.custom-scrollbar::-webkit-scrollbar-thumb {
  border-radius: 10px;
  box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.5);
}

  • 自定义文本选择的样式
<p class="custom-text-selection">Select some of this text.</p>
::selection {
  background: aquamarine;
  color: black;
}
.custom-text-selection::selection {
  background: deeppink;
  color: white;
}

  • 为文本提供渐变颜色
<p class="gradient-text">Gradient text</p>
.gradient-text {
  background: -webkit-linear-gradient(pink, red);
  -webkit-text-fill-color: transparent;
  -webkit-background-clip: text;
}

  • :not 伪选择器对于设置一组元素的样式非常有用,同时保留最后一个(指定的)元素的样式
<ul class="css-not-selector-shortcut">
  <li>One</li>
  <li>Two</li>
  <li>Three</li>
  <li>Four</li>
</ul>
.css-not-selector-shortcut {
  display: flex;
}
ul {
  padding-left: 0;
}
li {
  list-style-type: none;
  margin: 0;
  padding: 0 0.75rem;
}
li:not(:last-child) {
  border-right: 2px solid #d2d5e4;
}

  • css 开关
<input type="checkbox" id="toggle" class="offscreen" /> <label for="toggle" class="switch"></label>
.switch {
  position: relative;
  display: inline-block;
  width: 40px;
  height: 20px;
  background-color: rgba(0, 0, 0, 0.25);
  border-radius: 20px;
  transition: all 0.3s;
}
.switch::after {
  content: '';
  position: absolute;
  width: 18px;
  height: 18px;
  border-radius: 18px;
  background-color: white;
  top: 1px;
  left: 1px;
  transition: all 0.3s;
}
input[type='checkbox']:checked + .switch::after {
  transform: translateX(20px);
}
input[type='checkbox']:checked + .switch {
  background-color: #7983ff;
}
.offscreen {
  position: absolute;
  left: -9999px;
}
  • 创建一个鼠标悬停的边框动画
<div class="button-border"><button class="button">Submit</button></div>

.button {
  background-color: #c47135;
  border: none;
  color: #ffffff;
  outline: none;
  padding: 12px 40px 10px;
  position: relative;
}
.button:before,
.button:after {
  border: 0 solid transparent;
  transition: all 0.25s;
  content: '';
  height: 24px;
  position: absolute;
  width: 24px;
}
.button:before {
  border-top: 2px solid #c47135;
  left: 0px;
  top: -5px;
}
.button:after {
  border-bottom: 2px solid #c47135;
  bottom: -5px;
  right: 0px;
}
.button:hover {
  background-color: #c47135;
}
.button:hover:before,
.button:hover:after {
  height: 100%;
  width: 100%;
}
  • 甜甜圈旋转器
<div class="donut"></div>
@keyframes donut-spin {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}
.donut {
  display: inline-block;
  border: 4px solid rgba(0, 0, 0, 0.1);
  border-left-color: #7983ff;
  border-radius: 50%;
  width: 30px;
  height: 30px;
  animation: donut-spin 1.2s linear infinite;
}

  • 页面弹窗内容滚动,蒙层不滚动的问题
<div class="mask"></div>
<div class="pop">
    <div class="popContent">滚轮内容</div>
</div>

css:
.mask{
    postion:fixed:
    top:0;
    left:0;
    display:flex:
    justify-content:center;
    align-items:center;
    width:100%;
    height:100%;
    background-color:rgba(0,0,0,0.76);
    z-index:10
}
.pop{
    postion:absolute:
    top:0;
    left:0;
    right:0;
    bottom:0;
    width:100px;
    height:100px;
    background:red;
    box-sizing:border-box;
    border-radius:10px;
    color:#fff;
    -webkit-overflow-scrolling:touch;
    .popContent{
        
        
    }
    
}
  • 文字单列竖向排列
// 单列展示时
.wrap {
    width: 25px;
    line-height: 18px;
    height: auto;
    font-size: 12px;
    padding: 8px 5px;
    word-wrap: break-word;/*英文的时候需要加上这句,自动换行*/  
}
  • 修改input 输入框中 placeholder 默认字体样式
//webkit内核的浏览器 
input::-webkit-input-placeholder {
    color: #c2c6ce;
}
//Firefox版本4-18 
input:-moz-placeholder {
    color: #c2c6ce;
}
//Firefox版本19+
input::-moz-placeholder {
    color: #c2c6ce;
}
//IE浏览器
input:-ms-input-placeholder {
    color: #c2c6ce;
}

DOM

1.偏移量
    1.offsetHeight/offsetWidth // 元素宽高,包括滚动条,边框
    2.offsetLeft/offsetTop // 元素外边框到包含元素内边框的距离
    3.offsetParent //保存着包含元素的引用(具有大小的包含元素的引用)
// 获取元素在页面中的偏移量
function getElLeft(el){
    let actualLeft = el.offsetLeft;
    let current = el.offsetParent;
    while(current !== null){
        actualLeft += current.offsetLeft;
        current = current.offsetParent;
    }

    return actualLeft
}
// 注: 这些偏移量都是只读的,每次访问都要重新计算,因此最好将其保存到局部变量里,以提高性能

2.客户区大小clientWidth/clientHeight(元素内容及内边距所占据的空间)
    获取视口大小 clientW = document.body.clientWidth || document.documentElement.clientWidth;

3.滚动区大小
    1.scrollHeight //在没有滚动条的情况下,元素内容总高度(内容+内边距)
    2.scrollWidth
    3.scrollLeft //被隐藏在内容区域左侧的像素数
    4.scrollTop //被隐藏在内容区域上方的像素数,通过设置该值可以让滚动条滚动到响应位置
    *** 确定文档总高度兼容方案
    let scrollH = document.documentElement.scrollHeight || document.body.scrollHeight;
    let clientH = document.documentElement.clientHeight || document.body.clientHeight;
    let docHeight = Math.max(scrollH, clientH);

4.确定元素大小 getBoundingClientRect()
    该方法返回一个矩形对象,包括left,top,right,bottom属性,表示元素在页面中相对于视口的位置

  • 下拉组件编写时用到的dom api
 <div
    class="yo-scroll"
    :class="{'down':(refresState===0),'up':(refresState==1),refresh:(refresState===2),touch:touching}"
    @touchstart="touchStart($event)"
    @touchmove="touchMove($event)"
    @touchend="touchEnd($event)"
    @scroll="infiniteLoading ? onScroll($event) : undefined"
  >
    <div class="inner" :style="{ transform: 'translate3d(0, ' + top + 'px, 0)' }">
      <div class="pull-refresh">
          <span class="down-tip">下拉更新</span>
          <span class="up-tip">松开更新</span>
          <span class="refresh-tip">更新中</span>
      </div>
      <slot> </slot>
    </div>
  </div>
  
  touchstart:
  e.targetTouches[0].pageY 获取滚动开始时点击点的y轴坐标
  touchMove:
  e.targetTouches[0].pageY 滚动中点击点的y轴坐标

webpack


  • vue-cli2xx
  1. 打包不同环境包
packjson:
    命令: npm run build:xxx: node build/build.js xxx
<!--取到命令中指定的环境-->
const env = process.argv[2];
<!--修改node环境变量-->
process.env.NODE_ENV = env;
<!--修改api文件路径-->
1:删掉axios配置文件中的baseURL
2:新建baserUrl文件夹在api文件夹下
    文件夹中定义的若干个需要请求的ip地址,默认ip为代理地址
    根据不同环境给不同请求的ip配置
    抛出ip地址
    在不同的页面的接口文件中引入,将ip和接口地址拼接抛出
    这样就能不同命令打包出不同环境的包了
<!--解决静态文件404的问题-->