使用Set
处理数组去重和元素剔除问题
Set
是es6新增的一种数据结构,它和数组非常相似,但是成员的值都是唯一的,没有重复的值。它提供了4个语义化的API:
add(value)
:添加某个值,返回Set结构本身。delete(value)
:删除某个值,返回一个布尔值,表示删除是否成功。has(value)
:返回一个布尔值,表示该值是否为Set的成员。clear()
:清除所有成员,没有返回值。
参考自@阮一峰 老师的《ECMAScript 6 入门》
那么我们可以用Set
来干嘛呢?
第一个用法,数组去重。对于一个一维数组,我们可以先把它转化成Set
,再配合...
解构运算符重新转化为数组,达到去重的目的。请看例子:1
2
3
4
5
6
7const arr = [1, 1, 2, 2, 3, 4, 5, 5]
const newArr = [...new Set(arr)]
console.log(newArr)
// [1, 2, 3, 4, 5]
值得注意的是,这个方法不能对元素为“对象”的数组奏效:1
2
3
4
5
6
7const arr = [{ name: 'Alice', age: 12 }, { name: 'Alice', age: 12 }, { name: 'Bob', age: 13 }]
const newArr = [...new Set(arr)]
console.log(newArr)
// [{ name: 'Alice', age: 12 }, { name: 'Alice', age: 12 }, { name: 'Bob', age: 13 }]
这是因为Set
判断元素是否重复的办法类似于===
运算符,两个对象总是不相等的。
除了去重,Set
提供的delete()
方法也是非常实用。在以往的做法中,如果要删除数组中指定的元素,我们需要先获取该元素所在下标,然后通过splice()
方法去删除对应下标的元素,在理解上容易引起混乱:1
2
3
4
5
6
7
8
9// 我想删除数组当中值为2的元素const arr = [1, 2, 3]
const index = arr.indexOf(2)
if (index !== -1) {
arr.splice(index, 1)
}
console.log(arr)
// [1, 3]
使用Set
就清晰多了:1
2
3
4
5
6
7
8const arr = [1, 2, 3]
const set = newSet(arr)
set.delete(2)
arr = [...set]
console.log(arr)
// [1, 3]
使用map()
方法和对象解构语法提取字段
请求后台接口返回的数据中,很可能会遇到下面这种数据格式:1
2
3
4
5
6
7studentInfo = [
{ name: 'Alice', age: 18, no: 2 },
{ name: 'Bob', age: 16, no: 5 },
{ name: 'Candy', age: 17, no: 3 },
{ name: 'Den', age: 18, no: 4 },
{ name: 'Eve', age: 16, no: 1 },
]
当我们要获取姓名列表、年龄列表和编号列表的时候,我们可以通过map()
再配合对象的解构语法方便快捷地进行处理:1
2
3
4
5const nameList = studentInfo.map(({ name }) => name)
const ageList = studentInfo.map(({ age }) => age)
const noList = studentInfo.map(({ no }) => no)
// nameList: [ 'Alice', 'Bob', 'Candy', 'Den', 'Eve' ]// ageList: [ 18, 16, 17, 18, 16 ]// noList: [ 2, 5, 3, 4, 1 ]
使用filter()
方法和对象解构语法过滤数组
接上上面的例子,如果我想获取一个“年龄小于等于17岁”的新列表,应该怎么做呢?类似map()
方法,我们可以用filter()
方法进行过滤:1
2
3
4
5
6
7
8
9
10
11const newStudentInfo = studentInfo.filter(({ age }) => {
return age <= 17
})
/*
newStudentInfo: [
{ name: 'Bob', age: 16, no: 5 },
{ name: 'Candy', age: 17, no: 3 },
{ name: 'Eve', age: 16, no: 1 }
]
*/
借助includes()
方法求两个数组的差集
假设我们有以下两个数组:1
2var a = [1, 2, {s:3}, {s:4}, {s:5}]
var b = [{s:2}, {s:3}, {s:4}, 'a']
我们应该如何找到它们的差集呢?传统的方法可能需要把它们以Object形式hash化,但其实我们可以通过.includes()
方法更加优雅方便地找出差集,代码如下:1
2
3
4
5
6
7
8var a = [1, 2, {s:3}, {s:4}, {s:5}].map(item =>JSON.stringify(item))
var b = [{s:2}, {s:3}, {s:4}, 'a'].map(item =>JSON.stringify(item))
var diff = a.concat(b)
.filter(v => !a.includes(v) || !b.includes(v))
.map(item =>JSON.parse(item))
// diff: [1, 2, {s:5}, {s:2}, "a"]
至于为什么要JSON.stringify()
,是因为要对比两个“对象元素”是否相等,是无法直接以“对象”形式比较的(永远返回不相等)。
Array.includes 与条件判断
一般我们判断或用 ||1
2
3
4
5
6// condition
function test(fruit) {
if (fruit == "apple" || fruit == "strawberry") {
console.log("red");
}
}
如果我们有更多水果1
2
3
4
5
6
7function test(fruit) {
const redFruits = ["apple", "strawberry", "cherry", "cranberries"];
if (redFruits.includes(fruit)) {
console.log("red");
}
}
Set 与去重
ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。Set 本身是一个构造函数,用来生成 Set 数据结构。
数组去重1
2
3const arr = [3, 5, 2, 2, 5, 5];
const unique = [...new Set(arr)];
// [3,5,2]
Array.from 方法可以将 Set 结构转为数组。我们可以专门编写使用一个去重的函数1
2
3
4
5function unique(array) {
return Array.from(new Set(array));
}
unique([1, 1, 2, 3]); // [1, 2, 3]
字符去重1
2
3let str = [...new Set("ababbc")].join("");
console.log(str);
// 'abc'
另外 Set 是如此强大,因此使用 Set 可以很容易地实现并集(Union)、交集(Intersect)和差集(Difference)。1
2
3
4
5
6
7
8
9
10
11
12
13
14let a = new Set([1, 2, 3]);
let b = new Set([4, 3, 2]);
// 并集
let union = new Set([...a, ...b]);
// Set {1, 2, 3, 4}
// 交集
let intersect = new Set([...a].filter(x => b.has(x)));
// set {2, 3}
// 差集
let difference = new Set([...a].filter(x => !b.has(x)));
// Set {1}
Map 与字典类型数据
一般而已,JavaScript 实现字典数据是基于 Object 对象。但是 JavaScript 的对象的键只能是字符串。对于编程来说有很多不便。 ES6 提供了 Map 数据结构。它类似于 Object 对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值,字符串、数值、布尔值、数组、对象等等都可以当作键。1
2
3
4
5
6
7
8const resultMap = new Map()
.set(-1, {text:'小于',color:'yellow')
.set(0, {text:'等于',color:'black')
.set(1, {text:'大于',color:'green')
.set(null,{text:'没有物品',color:'red'})
let state = resultMap.get(null)
// {text:'没有物品',color:'red'}
Map 的遍历顺序就是插入顺序1
2
3
4
5
6
7
8
9
10
11
12
13const map = new Map([["F", "no"], ["T", "yes"]]);
for (let key of map.keys) {
console.log(key);
}
// "F"
// "T"
for (let value of map.value()) {
console.log(value);
}
// "no"
// "yes"
函数式的方式处理数据
按照我的理解,函数式编程主张函数必须接受至少一个参数并返回一个值。所以所有的关于数据的操作,都可以用函数式的方式处理。
假设我们有这样的需求,需要先把数组 foo 中的对象结构更改,然后从中挑选出一些符合条件的对象,并且把这些对象放进新数组 result 里。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32let foo = [
{
name: "Stark",
age: 21
},
{
name: "Jarvis",
age: 20
},
{
name: "Pepper",
age: 16
}
];
//我们希望得到结构稍微不同,age大于16的对象:
let result = [
{
person: {
name: "Stark",
age: 21
},
friends: []
},
{
person: {
name: "Jarvis",
age: 20
},
friends: []
}
];
从直觉上我们很容易写出这样的代码:1
2
3
4
5
6
7
8
9
10
11
12let result = [];
//有时甚至是普通的for循环
foo.forEach(function(person){
if(person.age > 16){
let newItem = {
person: person,
friends: [];
};
result.push(newItem);
}
})
使用函数式的写法,可以优雅得多1
2
3
4
5
6let result = foo
.filter(person => person.age > 16)
.map(person => ({
person: person,
friends: []
}));
数组求和1
2
3
4
5
6
7
8
9
10
11
12
13
14let foo = [1, 2, 3, 4, 5];
//不优雅
function sum(arr) {
let x = 0;
for (let i = 0; i < arr.length; i++) {
x += arr[i];
}
return x;
}
sum(foo); // => 15
//优雅
foo.reduce((a, b) => a + b); // => 15
compose 与函数组合
以下代码称为组合 compose1
2
3
4
5const compose = function(f, g) {
return function(x) {
return f(g(x));
};
};
由于函数式编程大行其道,所以现在将会在 JavaScript 代码看到大量的箭头()=>()=>()=>的代码。
ES6 版本 compose1
const compose = (f, g) => x => f(g(x));
在 compose 的定义中, g 将先于 f 执行,因此就创建了一个从右到左的数据 流。这样做的可读性远远高于嵌套一大堆的函数调用.
我们选择一些函数,让它们结合,生成一个崭新的函数。
reverse 反转列表, head 取列表中的第一个元素;1
2
3
4
5
6const head = arr => arr[0];
const reverse = arr => [].concat(arr).reverse();
const last = compose(head, reverse);
last(["jumpkick", "roundhouse", "uppercut"]);
// "uppercut"
但是我们这个这个compose不够完善,只能处理两个函数参数。redux源码有个很完备的compose函数,我们借鉴一下。1
2
3
4
5
6
7
8
9
10
11function compose(...funcs){
if (funcs.length === 0){
return arg => arg
}
if (funcs.length === 1 ){
return funcs[0]
}
return funcs.reduce((a,b)=>(...args) => a(b(...args)))
}
有了这个函数,我们可以随意组合无数个函数。现在我们增加需求,组合出一个lastAndUpper函数,内容是先reverse 反转列表, head 取列表中的第一个元素, 最后toUpperCase大写。1
2
3
4
5
6
7
8
9
10
11
12const head = arr => arr[0];
const reverse = arr => [].concat(arr).reverse();
const toUpperCase = str => str.toUpperCase();
const last = compose(head, reverse);
const lastAndUpper = compose(toUpperCase, head, reverse,);
console.log(last(["jumpkick", "roundhouse", "uppercut"]));
// "uppercut"
console.log(lastAndUpper(["jumpkick", "roundhouse", "uppercut"]))
// "UPPERCUT"
过去的一年, JavaScript
在持续变化着,其使用范围也越来越广。接下来,我将针对 JavaScript
的使用,列出 9 条 建议,以帮助你写出更加整洁高效的代码,成为更好的开发者。
async/await
JavaScript
极速发展的今天,回调地狱所产生的问题已不复存在。实际开发过程中我们应当尽量避免使用回调函数,除非为了遵守代码库规则或是维护性能。而解决回调地狱的一个常用方法为 Promise
,但在代码量较多时使用会适得其反。于是提出了 async / await
,使代码结构更加清晰明了,便于阅读和维护。一般而言,可以 await
任何 Promise
以防止正使用的库的返回值为 Promise
,也就是说 async/await
是 Promise
的语法糖,而且使用方法也十分简单:在函数前加 async
。下面是一个简单的例子:1
2
3
4
5
6
7async function getData() {
const result = await axios.get('https://dube.io/service/ping')
const data = result.data
console.log('data', data)
return data
}
getData()
await 只能使用在 async 函数中,不能用于全局作用域。
async/await
是 ES2017 中引入的,使用时请进行转换。
异步控制流
当我们进行异步调用并获得返回值时,通常期望直接获取多个数据集,并且分别操作每个数据集。因此有了以下方式:
for…of
假设页面上要展示 Pokemon 数据,可以通过 axios
获取它们的详细信息,我们所期望的是在得到返回值时立即更新页面中的所有数据,而不是等所有调用完成后才进行更新。
我们可以使用 for...of
解决上述问题。 首先循环遍历数组,并在每个循环内执行异步代码,当所有调用都成功时跳出循环。需要注意的是,这种方法虽然会对性能产生一些影响,但也不乏是一个很好的方法。
以下是一个例子:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18import axios from'axios'
let myData = [{ id: 0 }, { id: 1 }, { id: 2 }, { id: 3 }]
async function fetchData(dataSet) {
for (entry of dataSet) {
const result = await axios.get(`https://ironhack-pokeapi.herokuapp.com/pokemon/${entry.id}`)
const newData = result.data
updateData(newData)
console.log(myData)
}
}
function updateData(newData) {
myData = myData.map(el => {
if (el.id === newData.id) return newData
return el
})
}
fetchData(myData)
可以将这些例子复制粘贴到编辑器中调试运行。
译者注:除了循环本身带来的性能问题之外,在使用
async/await
处理异步请求时也会对性能造成影响:如果使用过多await
语句,而且候这些语句并不需要依赖于之前的语句,则会产生async/await
地狱,影响性能。
Promise.all
如果想要并行获取所有的 Pokemon,我们可以使用 Promise.all
方法来 await
所有 Promise
:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19import axios from'axios'
let myData = [{ id: 0 }, { id: 1 }, { id: 2 }, { id: 3 }]
async function fetchData(dataSet) {
const pokemonPromises = dataSet.map(entry => {
return axios.get(`https://ironhack-pokeapi.herokuapp.com/pokemon/${entry.id}`)
})
const results = awaitPromise.all(pokemonPromises)
results.forEach(result => {
updateData(result.data)
})
console.log(myData)
}
function updateData(newData) {
myData = myData.map(el => {
if (el.id === newData.id) return newData
return el
})
}
fetchData(myData)
for...of
和Promise.all
都是ES6+
引入的,使用时请进行转换。
解构赋值 & 默认值
回到上个例子:1
2const result = axios.get(`https://ironhack-pokeapi.herokuapp.com/pokemon/${entry.id}`)
const data = result.data
现在有一种更简单的方法来实现它:通过解构赋值的方式从对象或数组中获取一个或多个值:1
const { data } = await axios.get(...)
也可对变量重命名:1
const { data: newData } = await axios.get(...)
另一种方法是在解构赋值时指定默认值,这样做可以确保代码不会出现 undefined
,也避免手动检查变量的麻烦。1
2const { id = 5 } = {}
console.log(id) // 5
这些方法也可以用于函数参数,例如:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17function calculate({ operands = [1, 2], type = 'addition' } = {}) {
return operands.reduce((acc, val) => {
switch (type) {
case'addition':
return acc + val
case'subtraction':
return acc - val
case'multiplication':
return acc * val
case'division':
return acc / val
}
}, ['addition', 'subtraction'].includes(type) ? 0 : 1)
}
console.log(calculate()) // 3
console.log(calculate({ type: 'division' })) // 0.5
console.log(calculate({ operands: [2, 3, 4], type: 'multiplication' })) // 24
ES6
引入了解构赋值和默认值,使用时请进行转换。
真值和虚值
当我们使用默认值时,通常要对现有值进行一系列判断,这种方法使代码变得异常繁琐,而现在我们可以真值(Truthy
)和虚值(Falsy
)的方式来改进它,不仅可以节省代码量,还使人更加信服。
以下是之前的做法:1
2
3
4
5
6
7
8
9
10
11if (myBool === true) {
console.log(...)
}
// OR
if (myString.length > 0) {
console.log(...)
}
// OR
if (isNaN(myNumber)) {
console.log(...)
}
简化后:1
2
3
4
5
6
7
8
9
10
11if (myBool) {
console.log(...)
}
// OR
if (myString) {
console.log(...)
}
// OR
if (!myNumber) {
console.log(...)
}
以下为 Falsy
和 Truthy
的概念:
False
- 长度为0的字符串
- 数字
0
false
undefined
null
NaN
True
- 空数组
- 空对象
- 其他
使用真值和虚值时没有确切的比较方式,这类似于我们进行比较时常使用双等号 ==
而不是三等号 ===
。一般而言,这两者的判定方式相同,但在某些情况下也会遇到一些错误,对我来说主要为数字 0
。
逻辑运算符和三元运算符
逻辑运算符和三元运算符主要用于精简代码,有助于保持代码整洁度,但当他们形成运算链时会显得杂乱。
逻辑运算符
逻辑运算符:和(&&
)、或(||
),一般用于比较两个表达式,返回值为: true
、false
或着它的匹配值。如下例:1
2
3
4
5
6
7
8console.log(true && true) // true
console.log(false && true) // false
console.log(true && false) // false
console.log(false && false) // false
console.log(true || true) // true
console.log(true || false) // true
console.log(false || true) // true
console.log(false || false) // false
我们可以将逻辑运算符与真值和虚值的相关知识结合起来。
如果有表达式 A
和 B
,针对两种逻辑运算符,有以下规则:
A && B
: 当A
为false
时则直接返回A
的值 ;否则返回B
的值。A || B
: 当A
为true
时则直接返回A
的值 ;否则返回B
的值。
译者注:上述规则为逻辑运算中的短路现象。
1 | console.log(0 && { a: 1 }) // 0 |
三元运算符
三元运算符与逻辑运算符非常相似,但有由三个部分组成:
- 条件表达式:其结果为真值或是虚值
- 返回值 1:条件表达式为真值时,返回该值
- 返回值 2:条件表达式为虚值时,返回该值
例如:1
2
3
4const lang = 'German'
console.log(lang === 'German' ? 'Hallo' : 'Hello') // Hallo
console.log(lang ? 'Ja' : 'Yes') // Ja
console.log(lang === 'French' ? 'Bon soir' : 'Good evening') // Good eveing
自判断链接
当访问某个嵌套对象的属性时,由于不能确定目标对象或者属性性是否存在,而需要进行一系列判断:1
2
3let data
if (myObj && myObj.firstProp && myObj.firstProp.secondProp && myObj.firstProp.secondProp.actualData)
data = myObj.firstProp.secondProp.actualData
显而易见,代码变得非常臃肿难看。而自判断链接(optional chaining
)的提出,正好可以满足对嵌套属性的校验需求,并使代码更加清晰整洁。如下例:1
const data = myObj?.firstProp?.secondProp?.actualData
1 | function safeGet(o, path){ |
译者注:自判断链接: 检查一个对象上面是否存在某属性。
出现原因:调用某
Object
属性链中的某个属性时,如果该属性不存在,会导致Cannot read property xxx of undefined
错误。于是自判断链接?.
出现。使用方式:
obj?.a?.b?.c
。依次对代码中的属性进行判断,如果为null
或者undefined
时,结束调用,返回undefined
。
目前,自判断链接还未纳入官方规范中,只处于第一阶段的实验特性。您需要在
babelrc
中添加@ babel / plugin-proposal-optional-chaining
后方可使用它。
类属性 & 绑定
JavaScript
中经常会用到绑定(bind
)。ES6
规范中箭头函数的引入,使 JavaScript
开发人员有了一种将函数自动绑定到执行上下文中的常用方法,同时这种方法非常重要。
由于 JavaScript
中的类方法有特定的调用方式,因此当我们首次声明一个类时不能使用箭头函数,因此需要在其他位置进行函数绑定,比如在构造函数中(以 React.js
为例)。工作当中我总是先定义类方法再对其进行绑定,这种方法非常繁琐且容易出错。但如果使用 class
语法,我们可以通过箭头函数自动绑定它。以下是绑定 _increaseCount
的例子:1
2
3
4
5
6
7
8
9
10
11
12
13
14classCounterextendsReact.Component{
constructor(props) {
super(props)
this.state = { count: 0 }
}
render() {
return (
<div><h1>{this.state.count}</h1><buttononClick={this._increaseCount}>Increase Count</button></div>
)
}
_increaseCount = () => {
this.setState({ count: this.state.count + 1 })
}
}
目前,类属性还未纳入官方规范中,只处于第三阶段的实验特性。您需要在
babelrc
中添加@ babel / plugin-proposal-class-properties
后方可使用。
JavaScript开发技巧
String Skill
时间对比:时间个位数形式需补0
1 | const time1 = "2019-03-31 21:00:00"; |
格式化金钱:带小数无效
1 | const thousand = num => num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); |
生成随机ID
1 |
|
生成随机HEX色值
1 | const randomColor = () =>"#" + Math.floor(Math.random() * 0xffffff).toString(16).padEnd(6, "0"); |
生成星级评分
1 | const startScore = rate =>"★★★★★☆☆☆☆☆".slice(5 - rate, 10 - rate); |
操作URL查询参数
1 | const params = new URLSearchParams(location.search); // location.search = "?name=yajun&sex=female" |
Number Skill
取整:代替正数的
Math.floor()
,代替负数的Math.ceil()
1 | const num1 = ~~ 1.69; |
补零
1 | const fillZero = (num, len) => num.toString().padStart(len, "0"); |
转数值:只对
null、""、false、数值字符串
有效
1 | const num1 = +null; |
精确小数
1 | const round = (num, decimal) =>Math.round(num * 10 ** decimal) / 10 ** decimal; |
判断奇偶
1 | const num = 0; |
取最小最大值
1 | const arr = [0, 1, 2]; |
Boolean Skill
短路运算符
1 | const a = d && 1; // 满足条件赋值:取假运算,从左到右依次判断,遇到假值返回假值,后面不再执行,否则返回最后一个真值 |
是否为空数组
1 | const arr = []; |
是否为空对象
1 | const obj = {}; |
满足条件时执行
1 | const flagA = true; // 条件A |
为非假值时执行
1 | const flag = false; // undefined、null、""、0、false、NaN |
数组不为空时执行
1 | const arr = [0, 1, 2]; |
对象不为空时执行
1 | const obj = { a: 0, b: 1, c: 2 }; |
函数退出代替条件分支退出
1 | if (flag) { |
Array Skill
克隆数组
1 | const _arr = [0, 1, 2]; |
合并数组
1 | const arr1 = [0, 1, 2]; |
去重数组
1 | const arr = [...new Set([0, 1, 1, null, null])]; |
混淆数组
1 | const arr = [0, 1, 2, 3, 4, 5].slice().sort(() =>Math.random() - .5); |
交换赋值
1 | let a = 0; |
过滤空值:undefined、null、””、0、false、NaN
1 | const arr = [undefined, null, "", 0, false, NaN, 1, 2].filter(Boolean); |
异步累计
1 | asyncfunctionFunc(deps) { |
首部插入元素
1 | let arr = [1, 2]; // 以下方法任选一种 |
尾部插入元素
1 | let arr = [0, 1]; // 以下方法任选一种 |
统计元素个数
1 | const arr = [0, 1, 1, 2, 2, 2]; |
创建指定长度数组
1 | const arr = [...new Array(3).keys()]; |
创建指定长度且值相等的数组
1 | const arr = [...new Array(3).keys()].fill(0); |
reduce代替map和filter
1 | const _arr = [0, 1, 2]; |
Object Skill
克隆对象
1 | const _obj = { a: 0, b: 1, c: 2 }; // 以下方法任选一种const obj = { ..._obj }; |
合并对象
1 | const obj1 = { a: 0, b: 1, c: 2 }; |
对象字面量:获取环境变量时必用此方法,用它一直爽,一直用它一直爽
1 | const env = "prod"; |
创建纯空对象
1 | const obj = Object.create(null); |
解构嵌套属性
1 | const obj = { a: 0, b: 1, c: { d: 2, e: 3 } }; |
解构对象别名
1 | const obj = { a: 0, b: 1, c: 2 }; |
删除无用属性
1 | const obj = { a: 0, b: 1, c: 2 }; // 只想拿b和c |
Function Skill
函数自执行
1 | const Func = function() {}(); // 常用 |
隐式返回值:只能用于
单语句返回值箭头函数
,如果返回值是对象必须使用()
包住
1 | const Func = function(name) { |
一次性函数:适用于运行一些只需执行一次的初始化代码
1 | functionFunc() { |
惰性载入函数:函数内判断分支较多较复杂时可大大节约资源开销
1 | functionFunc() { |
检测非空参数
1 | functionIsRequired() { |
字符串创建函数
1 | const Func = newFunction("name", "console.log(\"I Love \" + name)"); |
优雅处理错误信息
1 | try { |
优雅处理Async/Await参数
1 | functionAsyncTo(promise) { |
优雅处理多个函数返回值
1 | asyncfunctiongetAll() { |
DOM Skill
显示全部DOM边框:调试页面元素边界时使用
1 | [].forEach.call($$("*"), dom => { |
自适应页面:页面基于一张设计图但需做多款机型自适应,元素尺寸使用
rem
进行设置
functionAutoResponse(width = 750) {
const target = document.documentElement;
target.clientWidth >= 600
? (target.style.fontSize = "80px")
: (target.style.fontSize = target.clientWidth / width * 100 + "px");
}