关于可选链操作符、隐藏元素的几种方案差异、Promise 配合 async/await 使用。
可选链操作符
近期项目负责人做出了一个规范修正:
if (json.ret && json.data.length > 0) {
const data = json.data
.......
}
改为
const data = json?.ret && json?.data || []
这个语法被称为可选链操作符
ES2020 内容,Chrome>80 版本可用。
语法
obj?.prop
obj?.[expr]
arr?.[index]
func?.(args)
逻辑
可选链操作符 ?.
可以按照操作符之前的属性是否有效,链式读取对象的属性或者使整个对象链返回 undefined
。?.
运算符的作用与 .
运算符类似,不同之处在于,如果对象链上的引用是 nullish
(null 或者 undefined),.
操作符会抛出一个错误,而 ?.
操作符则会按照短路计算的方式进行处理,返回 undefined
。可选链操作符也可用于函数调用,如果操作符前的函数不存在,也将会返回 undefined
。
动机
当尝试调用一个可能不存在的方法时也可以使用可选链。这将是很有帮助的,比如,当使用一个 API 的方法可能不可用时,要么因为实现的版本问题要么因为当前用户的设备不支持该功能。
函数调用时如果被调用的方法不存在,使用可选链可以使表达式自动返回 undefined 而不是抛出一个异常。
隐藏元素的几种方案差异
- display: none
- DOM 结构:浏览器不会渲染 display 属性为 none 的元素,会让元素完全从渲染树中消失,渲染的时候不占据任何空间;
- 事件监听:无法进行 DOM 事件监听,不能点击;
- 性能:修改元素会造成文档回流(reflow 与 repaint),读屏器不会读取 display: none 元素内容,性能消耗较大;
- 继承:是非继承属性,由于元素从渲染树消失,造成子孙节点消失,即使修改子孙节点属性子孙节点也无法显示,毕竟子类也不会被- 渲染;
- 场景:显示出原来这里不存在的结构;
- transition:transition 不支持 display。
- visibility: hidden
- DOM 结构:不会让元素从渲染树消失,渲染元素继续占据空间,只是内容不可见;
- 事件监听:无法进行 DOM 事件监听,不能点击;
- 性能:修改元素只会造成本元素的重绘(repaint),是重回操作,比回流操作性能高一些,性能消耗较少;读屏器读取 visibility: hidden 元素内容;
- 继承:是继承属性,子孙节点消失是由于继承了 visibility: hidden,子元素可以通过设置 visibility: visible 来取消隐藏;
- 场景:显示不会导致页面结构发生变动,不会撑开;
- transition:transition 支持 visibility,visibility 会立即显示,隐藏时会延时。
- opacity: 0
- DOM 结构:透明度为 100%,不会让元素从渲染树消失,渲染元素继续占据空间,只是内容不可见; 事件监听:可以进行 DOM 事件监听,可以点击;
- 性能:提升为合成层,是重建图层,不和动画属性一起则不会产生 repaint(不脱离文档流,不会触发重绘),性能消耗较少;
- 继承:会被子元素继承,且子元素并不能通过 opacity: 1 来取消隐藏;
- 场景:可以跟 transition 搭配;
- transition:transition 支持 opacity,opacity 可以延时显示和隐藏。
- rgba
- background:rgba(R, G, B, 0),只是背景颜色透明,元素透明,依然占据空间。
- background:rgba(R, G, B, 0)不会被子元素继承 依然能触发已经绑定的事件。
- transition 有效。
- z-index: -1
- 在元素当前 dom 脱离文档流(position:absolute)的前提下,设置 z-index 才起作用。
- 设置 z-index:-1 本质是改变当前 dom 的层叠上下文,使器置于其他元素之下,达到被隐藏的目的。 部分重排,不影响其他图层布局 被其他元素遮挡部分,无法响应事件,即使上层元素设置了 pointer-events:none;也无法点击到(注:这个属性会被继承的)
打个比方:
- display: none: 从这个世界消失了, 不存在了;
- opacity: 0: 视觉上隐身了, 看不见, 可以触摸得到;
- visibility: hidden: 视觉和物理上都隐身了, 看不见也摸不到, 但是存在的;
附加题:CSS 隐藏页面上的一个元素有哪几种方法?
- display:none,visibility:hiden,opacity:0 这三种;
- 设置 fixed 并设置足够大负距离的 left top 使其“隐藏”;
- 用层叠关系 z-index 把元素叠在最底下使其“隐藏”;
- 用 text-indent:-9999px 使其文字隐藏。
怎么把 Promise.then()中的数据拿出来?
因为我们没办法现在取出来自未来的东西,所以 js 中“一次异步 处处异步。”
现在要用 fetch 获取一段数据
原始写法:
getData(code) {
fetch('http://urlurlurl', {
method: 'POST',
credentials: 'same-origin',
headers: {
'Accept': 'application/json, text/javascript, */*; q=0.01',
'content-type': 'application/json'
},
body: JSON.stringify({ "type": code })
}).then(res => {
return res.json()
}).then(json => {
json // 只能在这里进行对json的操作。
})
}
这个函数的问题是,只能在内部进行对获取到的 json 的操作,如果在外部直接返回只能得到 undefined。
getData(code) {
let result = {};
fetch('http://urlurlurl', {
method: 'POST',
credentials: 'same-origin',
headers: {
'Accept': 'application/json, text/javascript, */*; q=0.01',
'content-type': 'application/json'
},
body: JSON.stringify({ "type": code })
}).then(res => {
return res.json()
}).then(json => {
result = json // 只能在这里进行对json的操作。
})
return result
}
let ans = this.getData(10) // ans is undefined.
尝试:
getData(code) {
return new Promise((resolve)=>{
fetch('http://urlurlurl', {
method: 'POST',
credentials: 'same-origin',
headers: {
'Accept': 'application/json, text/javascript, */*; q=0.01',
'content-type': 'application/json'
},
body: JSON.stringify({ "type": code })
}).then(res => {
resolve(res.json())
})
})
}
// 此时调用写法为
this.getData(10).then(data=>{return data})
上面这个写法 data 还是在 then 里,没有在实质上解决问题。
引入 ES7 async/await:
async getData1(code) {
// return new Promise((resolve)=>{
let res = await fetch('http://urlurlurl', {
method: 'POST',
credentials: 'same-origin',
headers: {
'Accept': 'application/json, text/javascript, */*; q=0.01',
'content-type': 'application/json'
},
body: JSON.stringify({ "type": code })
})
// let json = await res.json()
return res.json()
}
// 获取数据
async function(){
let res = await getData1(10)
}
此时 res 已经可以获取到了。
推荐方式
(以下示例代码在 node 平台上运行)
- 写法 1
// bash: npm install node-fetch
const fetch = require('node-fetch')
const api = "https://wis.qq.com/weather/common?source=pc&province=%E4%B8%8A%E6%B5%B7&city=%E4%B8%8A%E6%B5%B7&county=%E5%BE%90%E6%B1%87&weather_type=observe";
// 方法非异步
function getData(_api){
return new Promise((resolve)=>{
fetch(_api).then(req=>{
return req.json()
}).then(json=>{
resolve(json.data)
})
})
}
// 调用异步
async function init(){
let data = await getData(api)
console.log(data)
}
init();
- 写法 2
// bash: npm install node-fetch
const fetch = require('node-fetch')
const api = "https://wis.qq.com/weather/common?source=pc&province=%E4%B8%8A%E6%B5%B7&city=%E4%B8%8A%E6%B5%B7&county=%E5%BE%90%E6%B1%87&weather_type=observe";
// 方法异步
async function getData(_api){
let req = await fetch(_api)
let json = await req.json()
return json.data
}
// 调用非异步
function init(){
getData(api).then(data=>{
console.log(data)
})
// console.log(getData(api)) // 返回 Promise { <pending> }
}
init();