VUE
el-table 合并相同列 span-method
mounted(){
let data2 = this.mergeTableRow(this.specList, ["color"]);
this.specList = data2
}
methods: {
// 表头合并
handerMethod({ row, column, rowIndex, columnIndex }){
if (row[0].level == 1) {
//这里有个非常坑的bug 必须是row[0]=0 row[1]=2才会生效
row[1].colSpan = 2
if (columnIndex === 0) {
return { display: 'none' }
}
}
},
// 行合并
SpanMethod({ row, column, rowIndex, columnIndex }){
const span = column["property"] + "-span";
if (row[span]) {
return row[span];
}
},
// 合并table Row
mergeTableRow(data, merge) {
if (!merge || merge.length === 0) {
return data;
}
merge.forEach((m) => {
const mList = {};
data = data.map((v, index) => {
const rowVal = v[m];
if (mList[rowVal] && mList[rowVal].newIndex === index) {
mList[rowVal]["num"]++;
mList[rowVal]["newIndex"]++;
data[mList[rowVal]["index"]][m + "-span"].rowspan++;
v[m + "-span"] = {
rowspan: 0,
colspan: 0,
};
} else {
mList[rowVal] = { num: 1, index: index, newIndex: index + 1 };
v[m + "-span"] = {
rowspan: 1,
colspan: 1,
};
}
return v;
});
});
return data;
}
}
预处理器 /deep/ 与 ::v-deep
1、/deep/
项目中用到了预处理器scss、sass、less操作符 >>> 可能会因为无法编译而报错。可以使用 /deep/,但是vue-cli3可能会导致编译报错。这个时候用::v-deep
2、::v-deep
::v-deep 在预处理器scss、sass、less 比较通用
3、vue3 ::v-deep 已经弃用,使用:deep
子组件里面自己引用自己
MyColumn.vue
<template>
<el-table-column :prop="titledata.values"
:label="titledata.label"
align="left">
<template v-if="titledata.children.length">
<my-column v-for="(item, index) in titledata.children"
:key="index"
:titledata="item"></my-column>
//自己引用自己把item作为titledata的数据传给自己再去走当前的页面,知道titledata.children的长度为0
</template>
</el-table-column>
</template>
<script>
export default {
name: 'MyColumn', //在这里name就相当于注册了当前的组件。可以直接引用自己了。
props: {
titledata: {
type: Object
}
}
}
</script>
vue model属性
实现自定义组件双向数据绑定
实现双向绑定的关键代码就是定义model属性中的prop和event,v-model中的值传递给model属性中的prop对应的变量。然后在自定义组件中选择一个要传递出去值的基本组件,通过$emit发送event事件并传递一个结果值,这样外部的v-model就收到了传出的值,因此就实现了双向传递。
<!--父组件 -->
<template>
<div id="box">
<span style="color: red">{{ message }}</span>
<!-- v-model 传过去子组件的model属性 -->
<myComponents v-model="message"></myComponents>
</div>
</template>
<script>
import myComponents from "../components/myComponent.vue";
export default {
components: {
myComponents,
},
data() {
return {
message: "11",
};
}
};
</script>
<!-- 子组件 -->
<template>
<div class="wrapper">
<input
type="text"
:value="message"
@input="$emit('eee', $event.target.value + ' <<==')"
/>
</div>
</template>
<script>
export default {
model: {
// vue2 默认 一个组件上的 v-model 默认会利用名为 value 的 prop 和名为 input 的事件
prop: 'vue',
event: 'input',
// vue3 默认情况下,v-model 在组件上都是使用 modelValue 作为 prop,并以 update:modelValue 作为对应的事件
prop: "message", // 跟父组件 v-model 的值做数据绑定
event: "eee", // 通过$emit 把值发送出去 为了避免事件名称冲突 此处可以自定义方法名
},
props: {
message: {
// 接收父组件传过来的值
type: String,
default: "",
},
}
};
</script>
vue中v-model详解
首屏幕加载速度慢
ES6
es6之数组的flat(),flatMap()
数组的成员有时还是数组,Array.prototype.flat()用于将嵌套的数组“拉平”,变成一维数组。该方法返回一个新数组,对原数据没有影响
[1, 2, [3, 4]].flat()
// [1, 2, 3, 4]
上面代码中,原数组的成员里面有一个数组,flat()方法将子数组的成员取出来,添加在原来的位置。
flat()默认只会“拉平”一层,如果想要“拉平”多层的嵌套数组,可以将flat()方法的参数写成一个整数,表示想要拉平的层数,默认为1。
[1, 2, [3, [4, 5]]].flat()
// [1, 2, 3, [4, 5]]
[1, 2, [3, [4, 5]]].flat(2)
// [1, 2, 3, 4, 5]
上面代码中,flat()的参数为2,表示要拉平两层的嵌套数组。
如果不管有多少层嵌套,都要转成一维数组,可以用Infinity关键字作为参数。
[1, [2, [3]]].flat(Infinity)
// [1, 2, 3]
如果原数组有空位,flat()方法会跳过空位。
[1, 2, , 4, 5].flat()
// [1, 2, 4, 5]
flatMap()方法对原数组的每个成员执行一个函数,相当于执行Array.prototype.map(),然后对返回值组成的数组执行flat()方法。该方法返回一个新数组,不改变原数组。
// 相当于 [[2, 4], [3, 6], [4, 8]].flat()
[2, 3, 4].flatMap((x) => [x, x * 2])
// [2, 4, 3, 6, 4, 8]
flatMap()只能展开一层数组。
空值合并运算符
空值合并操作符(??)是一个逻辑操作符,当左侧的操作数为 null 或者 undefined 时,返回其右侧操作数,否则返回左侧操作数。
const foo = null ?? 'default string';
console.log(foo);
// expected output: "default string"
const baz = 0 ?? 42;
console.log(baz);
// expected output: 0
注意
不能与 AND 或 OR 操作符共用
将 ?? 直接与 AND(&&)和 OR(||)操作符组合使用是不可取的。(译者注:应当是因为空值合并操作符和其他逻辑操作符之间的运算优先级/运算顺序是未定义的)这种情况下会抛出 SyntaxError
null || undefined ?? "foo"; // 抛出 SyntaxError
true || undefined ?? "foo"; // 抛出 SyntaxError
但是,如果使用括号来显式表明运算优先级,是没有问题的:
(null || undefined ) ?? "foo"; // 返回 "foo"
与可选链式操作符(?.)的关系
空值合并操作符针对 undefined 与 null 这两个值,可选链式操作符(?.) 也是如此。在这访问属性可能为 undefined 与 null 的对象时,可选链式操作符非常有用
let foo = { someFooProp: "hi" };
console.log(foo.someFooProp?.toUpperCase()); // "HI"
console.log(foo.someBarProp?.toUpperCase()); // undefined
空值合并运算符与可选链式操作符(?.)浏览器兼容性
Object.values
一个部门JSON数据中,属性名是部门id,属性值是个部门成员id数组集合,现在要把有部门的成员id都提取到一个数组集合中。
const deps = {
'采购部':[1,2,3],
'人事部':[5,8,12],
'行政部':[5,14,79],
'运输部':[3,64,105],
}
let member = Object.values(deps).flat(Infinity);
其中使用Infinity作为flat的参数,使得无需知道被扁平化的数组的维度
补充
flat方法不支持IE浏览器
关于异步函数
异步函数很常见,经常是用 Promise 来实现
const fn1 = () =>{
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1);
}, 300);
});
}
const fn2 = () =>{
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(2);
}, 600);
});
}
const fn = () =>{
fn1().then(res1 =>{
console.log(res1);// 1
fn2().then(res2 =>{
console.log(res2)
})
})
}
吐槽
如果这样调用异步函数,不怕形成地狱回调啊!
改进
const fn = async () =>{
const res1 = await fn1();
const res2 = await fn2();
console.log(res1);// 1
console.log(res2);// 2
}
补充
但是要做并发请求时,还是要用到Promise.all()
const fn = () =>{
Promise.all([fn1(),fn2()]).then(res =>{
console.log(res);// [1,2]
})
}
如果并发请求时,只要其中一个异步函数处理完成,就返回结果,要用到Promise.race()
使用 !! 操作符
!! 运算符可用于将表达式的结果快速转换为布尔值(true或false):
const greeting = 'Hello there!';
console.log(!!greeting) // true
const noGreeting = '';
console.log(!!noGreeting); // false
字符串和整数转换
使用 + 操作符将字符串快速转换为数字:
const stringNumer = '123';
console.log(+stringNumer); //123
console.log(typeof +stringNumer); //'number'
要将数字快速转换为字符串,也可以使用 + 操作符,后面跟着一个空字符串:
const myString = 25 + '';
console.log(myString); //'25'
console.log(typeof myString); //'string'
这些类型转换非常方便,但它们的清晰度和代码可读性较差。所以实际开发,需要慎重的选择使用。
检查数组中的假值
大家应该都用过数组方法:filter、some、every,这些方法可以配合 Boolean 方法来测试真假值。
const myArray = [null, false, 'Hello', undefined, 0];
// 过滤虚值
const filtered = myArray.filter(Boolean);
console.log(filtered); // ['Hello']
// 检查至少一个值是否为真
const anyTruthy = myArray.some(Boolean);
console.log(anyTruthy); // true
// 检查所有的值是否为真
const allTruthy = myArray.every(Boolean);
console.log(allTruthy); // false
下面是它的工作原理。我们知道这些数组方法接受一个回调函数,所以我们传递 Boolean 作为回调函数。Boolean 函数本身接受一个参数,并根据参数的真实性返回 true 或 false。所以:
myArray.filter(val => Boolean(val));
等价于:
myArray.filter(Boolean);
Object.entries
大多数开发人员使用 Object.keys 方法来迭代对象。 此方法仅返回对象键的数组,而不返回值。 我们可以使用 Object.entries 来获取键和值。
const person = {
name: '前端小智',
age: 20
};
Object.keys(person); // ['name', 'age']
Object.entries(data); // [['name', '前端小智'], ['age', 20]]
为了迭代一个对象,我们可以执行以下操作:
Object.keys(person).forEach((key) => {
console.log(`${key} is ${person[key]}`);
});
// 使用 entries 获取键和值
Object.entries(person).forEach(([key, value]) => {
console.log(`${key} is ${value}`);
});
// name is 前端小智
// age is 20
上述两种方法都返回相同的结果,但 Object.entries 获取键值对更容易。
逻辑赋值运算符
逻辑赋值运算符是由逻辑运算符&&、||、??和赋值运算符=组合而成。
const a = 1;
const b = 2;
a &&= b;
console.log(a); // 2
// 上面等价于
a && (a = b);
// 或者
if (a) {
a = b
}
检查a的值是否为真,如果为真,那么更新a的值。使用逻辑或 ||操作符也可以做同样的事情。
const a = null;
const b = 3;
a ||= b;
console.log(a); // 3
// 上面等价于
a || (a = b);
使用空值合并操作符 ??:
const a = null;
const b = 3;
a ??= b;
console.log(a); // 3
// 上面等价于
if (a === null || a === undefined) {
a = b;
}
注意: ??操作符只检查 null 或 undefined 的值。
虚线渐变色
@supports (-webkit-mask: none) or (mask: none) {
.contentItem::after {
border: none;
background: linear-gradient(0deg, #009EFF 0%, #46F1E9 100%) no-repeat;
mask: linear-gradient(to right, #000 6px, transparent 6px) repeat-x,
linear-gradient(to bottom, #000 6px, transparent 6px) repeat-y,
linear-gradient(to right, #000 6px, transparent 6px) repeat-x 0 100%,
linear-gradient(to bottom, #000 6px, transparent 6px) repeat-y 100% 0;
mask-size: 8px 2px, 2px 8px, 8px 2px, 2px 8px;
}
}
面向对象
特点:抽象、封装、继承、多态
XSS攻击
react 底层源码对防止XSS攻击做了处理
1、反射型
概念:基于反射的XSS攻击,主要依靠站点服务端返回脚本,在客户端触发执行从而发起Web攻击。
理解:攻击者构造出特殊的URL,其中包含恶意代码,页面中读取这个URL上拼接的恶意代码,并执行
开发措施:
- 前端在显示服务端的数据时,不仅是标签内容需要过滤、转义,连属性值可能也要
- 后端接收请求时,验证请求是否为攻击请求,攻击则屏蔽
2、存储型
概念:攻击者构造出特殊的URL,其中包含恶意代码,页面中读取这个URL上拼接的恶意代码,并执行
理解:用户将恶意代码提交到数据库,当用户打开目标网站时,数据库的恶意代码被读取,插入到页面代码中
开发措施:
- 首要是服务端要进行过滤,前端校验可以被绕过
- 当服务端不校验时,前端要以各种方式过滤里面可能的恶意脚本,例如script标签,将特殊字符转换成HTML编码。
3、DOM型
概念:基于DOM或本地的XSS攻击。一般是提供一个免费的wifi,但是提供免费wifi的网关会往你访问的任何页面插入一段脚本或者是直接返回一个钓鱼页面,从而植入恶意脚本。这种直接存在于页面,无须经过服务器返回就是基于本地的XSS攻击。
理解:也是由攻击者构造出特殊的URL,其中包含恶意代码。这个恶意代码未经过后端直接在前端执行。类似于反射型XSS,区别在于反射型XSS经过了服务端,属于服务端安全漏洞。这攻击其实跟网站本身没有什么关系,只是数据被中间人获取了而已,而由于HTTP是明文传输的,所以是极可能被窃取的。
开发措施:
- 使用https,HTTPS会在请求数据之前会进行一次握手,使得客户端和服务端都会有一个私钥,服务端用这个私钥加密,客户端用这个私钥解密,这样子数据被人截取了,也是一个加密的数据
如何防御
输入过滤 根据具体情况 我们可以在输入侧过滤,也可以在输出侧过滤
改成纯前端渲染,把代码和数据分开
如果插入到HTML中,需要做充分的转义
开启Content Security Policy(CSP)策略,CSP策略可以禁止加载外域脚本,禁止外域提交,禁止内联脚本执行,禁止未授权的脚本执行,合理上报及时发现XSS
输入长度控制
开启cookie的http-only,禁止js脚本读取某些敏感Cookie
验证码:防止冒充用户提交危险操作
overflow-x: overlay属性学习
auto时滚动条会占用宽度, 布局会发生变化
overlay时滚动条会覆盖在内容上, 会遮挡一部分内容
ps:css:overflow-x: overlay火狐浏览器不生效没有滚动条出现
解决方案:
在 overflow-x: overlay;的基础上加个 overflow: auto;适配,但是多出来的那一块还会存在,但是首要问题是解决没有滚动条,多出来的一点无所谓,因为仅限火狐会有
对象是无序的,会根据开头的第一个字符自动排序,要换成1,2,3这样子
mouseleave 和 mouseout 的区别
mouseleave 和 mouseout 均在离开相应元素的 border box 时被触发。
mouseleave 仅在指针离开元素时被触发,不冒泡;而 mouseout 在指针离开元素或进入该元素的子元素时均会被触发,冒泡。
打印样式
<style media="print" lang="scss">
@page {
size: auto;
margin: 3mm;
}
@media print {
}
</style>