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,应该是热加载?
- 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);
- 全局过滤器
- src下新建filter文件夹中新建index.js
<!--具体的过滤器方法-->
export const yuan = value =>
isNumber(value) ? `¥${(value / 100).toFixed(2)}` : value;
<!--挂载方法在vue全局过滤器中-->
export default {
install(Vue) {
Vue.filter('yuan', yuan);
}
};
- main.js中使用方法
import filters from '@/filter';
Vue.use(filters);
- 方法的使用,作用:保存组件的状态,回退后组件不刷新
- 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>
- :key="$route.fullPath"的作用是页面跳转当前页面地址的时候保证页面刷新
- $route.meta.keepAlive区分当前地址是不是需要保存组件状态
- 在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项目封装
- 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()使用)
- 黑子做的页面转pdf的功能 黑子的有道地址
- 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
- 打包不同环境包
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的问题-->