随手记录备忘
随手记录一些看到的点。
正篇
with 表达式
Q: 昨天看到个面试题,问with
表达式的利弊,还问为什么Vue
的编译函数里一直用到这个语法;
A1: with
语句是一个用于扩展作用域链的语法,表达式语法:
1
2
3
with (expression) {
statement;
}
大家都知道 JS 找变量的过程是顺着作用域链往上找的,with
语句将某个对象expression
添加到作用域链的顶部,如果statement
中的未命名空间变量,和作用域链的属性同名,则这个变量就指向了同名属性。
利弊:
- 性能层面:语句在不造成性能损失的情况下,减少变量长度,语句造成的附加计算很少,利用
with
可以减少不必要的指针路径解析运算。什么意思呢,就是平常我们访问对象属性时,经常有对象路径太长的情况,obj.prop1.child2.foo.bar
这种情况,用with
语法
1
2
3
with (obj.prop1.child2.foo) {
console.log(bar);
}
事实上这样写就会造成性能问题了,写在大括号里的属性,都会在指定作用域中找一遍,如果没找到,那就再沿着本来的作用域链向上找一遍。
- 语义层面:其实用
with
有不少 bug 的,而且语义不清晰;
1
2
3
4
5
6
7
8
9
10
11
12
13
let o = {};
with (o) {
a = 10; // 本来写这个是想让o.a = 10,其实解释器执行的时候会认为在o里面找不到a,那就会在window上添加a;
}
o.a; // undefined
window.a; // 10
function f(foo, bar) {
with (bar) {
console.log(foo);
// 这谁能知道会输出啥啊,如果bar有foo这个属性,就会输出bar.foo的值,如果没有,就会输出第一个参数foo
}
}
所以可以看到自己平常写代码的时候尽量少点用with
了,不能保证实际效果,可以直接用临时变量代替咯。let tmp = obj.prop1.child2.foo
A2: 那为什么在vue-template-compiler
里面还大量用了这个表达式呢(with(this) {}
),你还记得平常在template
模板里面写 JS 的时候不用加作用域链限制前缀this
之类的吗,这就是with
的作用了。
重点:不需要在编译template
的时候去分析模板里面的 JS 语句的作用域,直接交给with
就能拥有正确的作用域。所以可以看出是不是在模板 JS 里面最好不要调用全局方法啊,不然在当前对象找不到该方法,还得去全局作用域链里面再找一次。
事实上,上述的这些特点都是在浏览器直接引入Vue.js
时才存在的,我们知道既可以直接引入依赖包,也可以使用工程化的方案在构建时编译好;直接在浏览器环境引入的话,因为考虑到写个完整的AST
会导致依赖包太大,会拖慢节奏;如果使用工程化构建,会使用完整的AST
编译语句,就会剔除掉with
,所以只是会导致打包编译的过程变慢,但最终生成的代码没有with
。
附上尤大在知乎的解答:如何看待 Vue.js 2.0 的模板编译使用了with(this)
的语法? - 尤雨溪的回答 - 知乎
Vue 使用 window.onresize
一般我都避免使用的,想用其他方式解决,但实在不得不用的时候要注意,如果不在Vue
里面,要记得使用防抖函数;在Vue
里使用的话要记得使用$nextTick
,不然一直被触发,耗性能;另外记得在当前页面(或组件)销毁时将该函数置为空,在下个页面大概率用不着,或者压根就会报错。
1
2
3
4
5
6
7
8
9
10
mounted() {
window.onresize = () => {
this.$nextTick(() => {
// do something
})
}
}
beforeDestroy() {
window.onresize = null
}
记录一个 node | npm 拒绝访问的问题
原因不多赘述了,直接看现象;版本如下,报错如下,另外 win 环境安装的是 64 位的
1
2
3
4
5
6
7
8
node -v
v14.5.0
npm -v
拒绝访问
6.14.5
npm run dev
拒绝访问
然后就是一堆报错信息
搜了下好像都说是权限问题,但是在 win 环境下用管理员权限,cmd
和powershell
都还是报错;最后咋解决的呢,换了个 32 位的.msi
的安装包,至今费解,因为刚开始报错的时候我也重装过好几次node
,还分别用了好几种用户类型去安装,最后装了个 32 位的,就成了,就很怪,仅做记录。
统计页面停留时间
通过监听路由变化(或判断变化的 url 是否为不同的页面),来统计停留时间,还需要减去窗口最小化的时间;
- 监听路由变化(针对 SPA):
- history 模式又分为两类:监听
pushState | replaceState
或监听popstate
;popstate
事件会在用户手动前进或者后退浏览器时被触发(手动调用history.go | back | forward
也会触发);而pushState
不会触发该事件,所以要结合起来用; - hash 模式直接监听
hashchange
事件即可;
- history 模式又分为两类:监听
对pushState | replaceState
方法的改写,使得能够触发我们需要的监听函数;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
let _wr = function (type) {
let orig = window.history[type];
return function () {
let rv = orig.apply(this, arguments);
let e = new Event(type.toLowerCase());
e.arguments = arguments;
window.dispatchEvent(e);
return rv;
};
};
window.history.pushState = _wr('pushState');
window.history.replaceState = _wr('replaceState');
window.addEventListener('pushstate', function (event) {});
window.addEventListener('replacestate', function (event) {});
对hashchange
事件监听:
1
2
3
4
5
6
7
window.addEventListener(
'hashchange',
() => {
// do something
},
false
);
- 监听活跃状态的切换
通过page Visibility API
或者可以监听window.onblur | onfocus
事件;
1
2
3
document.addEventListener('visibilitychange', function (event) {
console.log(document.hidden, document.visibilityState);
});
- 何时上传数据到服务器
存储到本地缓存里,在下次打开时再上传;考虑到如果仅去监听window.onload | onbeforeunload
方法,有可能请求还没发送完页面就关闭了;而存储本地的话耗时会短的多,本地存储时还需要加上一些时间戳,进入时间、离开时间等。
依赖注入相关
一般我们提及依赖注入,会想到在 Java 的 spring 框架中用的多;依赖注入其实是一种设计模式,简单来说可以总结为:当资源互相依赖时,我们可以把资源的管控权释放给一个全局的容器(我们一般称为 IOC),需要资源的时候去这个容器里面取就行了。
很多时候资源在代码里体现的是某个类,当类之间相互依赖时,我们就会把类丢到容器里(这个过程称为注入),需要的时候就由容器去实例化类得到类实例,返回给使用者;注意这里重点就是我们要把实例化类的工作交给容器,返回给使用者的只能是一个类的实例;
这是因为如果类 A 被多个类同时依赖,万一 A 要修改自身的定义(这种情况很常见),而我们把实例化的工作交给了 A 的使用者,那我们就要深入到使用者的代码里,去修改实例化的代码,换种思路如果我们把实例化的工作交给第三方,那这样修改的时候只要改第三方的代码即可;这就像是黑盒,对使用者来说,只要拿到实例就可以用了,不用关心类是怎么实现的。
由上述定义可以得出,在依赖注入设计模式里,至少有三个角色:容器(IOC),资源提供者(Provider),资源使用者(User);整个调用过程:provider 通过 IOC 的某个方法,将自身注册到 IOC 中,User 通过 IOC 拿到所需要的资源,完成调用;
eslint / prettier
知乎看到一篇感觉不错的,写的挺全的:使用 Prettier 统一格式化项目代码 - AnLi 的文章 - 知乎 https://zhuanlan.zhihu.com/p/87586114
项目中遇到了忽略某些格式化代码的问题,记录一下:
-
eslint 忽略代码
- 对于路径或者某些文件,在项目根目录下建立
.eslintignore
文件,在文件里添加路径或者文件夹即可; - 文件顶部加入注释
/* eslint-disable */
可以忽略整个文件; - 忽略代码块可以用
/* eslint-disable */ | /* eslint-enable */
包裹起来;还可以添加参数,表示忽略某条具体的规则:/* eslint-disable no-alert, no-console */
; - 指定行:写在同一行:
// eslint-disable-line
;忽略下一行:// eslint-disable-next-line
;同样可以添加参数来忽略某些规则;
- 对于路径或者某些文件,在项目根目录下建立
-
prettier 忽略代码
- 和
eslint
类似,忽略路径.prettierignore
; - 忽略行:
// prettier-ignore
;
- 和