导师亲传的前端骚操作(持续更新~)
导师说过,前端技术无边界,唯一限制的,是自己的想象力!本文记录了导师把他多年前端工作传授给我的骚操作、思路总结。传内不传外!有一些技术点可能在平时的开发中很少使用,但是在解决某一类问题时十分管用,好使;有一些技术点如今看来可能已经过时,但是也展示了当时人对于当时问题的解决方案。
webpack项目动态配置代理地址
proxy参数的官方文档:webpack.docschina.org/configurati…
开发webpack项目时,我们可以通过配置proxy参数,代理到后台环境,进行前后端联调,比如下面的配置就是把带api前缀的请求转发给http://localhost:3000。
module.exports = {
//...
devServer: {
proxy: {
'/api': {
target: 'http://localhost:3000',
pathRewrite: { '^/api': '' },
},
},
},
};
以上配置存在这么一个优化点,就是后台的地址不应该是不可变的,不然在前后端联调过程中,后台换端口换地址的话,前端还得重启webpack项目(项目如果比较大的话,重启就需要些耐心了)。所以我们要改成动态可配置的代理地址,webpack-dev-server有一个router参数配置,支持传入函数,注意router的配置会覆盖target参数的配置。思路就是新增一个env文件,在router参数配的函数里读取env文件的配置,这样每次执行router时都会获取到最新的env配置,配置代码如下:
module.exports = {
//...
devServer: {
proxy: [
{
context: () => true, // 所有请求都会转发
router: () => fs.readFileSync('./env', 'utf-8'),
},
],
},
};
const http = require('http');
const server = http.createServer((request, response) => {
console.log('有人访问了服务器')
response.write('Hello, My Love')
response.end()
})
server.listen(3030);
const server2 = http.createServer((request, response) => {
console.log('有人访问了服务器2')
response.write('Hello, My Love')
response.end()
})
server2.listen(4040);
查找 bug
场景:有时我们想从万千行代码中,找出某js对象的某个属性在哪里被修改了,但是呢,这个js对象数据可能在十几个文件(每个文件可能有千行代码)中间被传递了不知道多少层,调用栈很深,仅靠查看调用链是很难分析出来的(老项目是可能达到这种复杂度的,笔者就曾经遇到过),那我们就可以借助Object.defineProperty、Proxy来帮我们找出属性被修改处。
Object.defineProperty(obj, prop, descriptor):
Object.defineProperty()方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。其存取描述符set可以提供一个 setter 方法,在属性值被修改时,会触发 setter 方法。
new Proxy(target, handler):
Proxy对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义。
// 找出哪里把xxx.submitData给重置掉了,当执行xxx.submitData = { a: 1 }会触发断点
Object.defineProperty(xxx, "submitData", {
set() {
debugger;
},
});
let handler = {
set(obj, prop, v) {
if (prop === "sfv" && v === "2022") {
// 当属性sfv被修改为2022时触发断点
debugger;
}
if (prop === "idux" && v === undefined) {
// 当扩展一个新属性idux时触发断点
debugger;
}
return v;
},
};
xxx.submitData = new Proxy(xxx.submitData, handler);
还有这里说一句,变量命名尽量语义化场景化,不建议所有表单数据都叫submitData,不然后面排查问题搜submitData,搜出来几十处还得一个个排除,submitData语义化也不够好,不知道是什么类型的表单数据。建议可以叫submitXxxData,比如路由器表单数据就叫submitRouterData。
使用 mixin 实现逻辑分离
混入 (mixin) 提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能。嗯,我们常常使用 mixin 来实现逻辑的复用,这里我想说的是或许 mixin 也可以用来实现逻辑的分离。
Vue3 引入了 Composition API 作为编写 vue 组件的新方式,用 vue2 的项目我们可以使用 mixin 来实现 Composition API 类似的代码抽离、逻辑分治。例子如下:
const useMouse = () => {
const x = ref(0);
const y = ref(0);
const updatePos = (e) => {
x.value = e.pageX;
y.value = e.pageY;
};
onMounted(() => {
window.addEventListener("mousemove", updatePos);
});
onUnMounted(() => {
window.removeEventListener("mousemove", updatePos);
});
return {
x,
y,
};
};
export default defineComponent({
setup() {
// code...
const { x, y } = useMouse();
// code...
},
});
@Component
class MouseMixin extends Vue {
protected x = 0;
protected y = 0;
private updatePos (e) {
this.x.value = e.pageX;
this.y.value = e.pageY;
}
protected mounted () {
window.addEventListener("mousemove", this.updatePos);
}
protected beforeDestroy () {
window.removeEventListener("mousemove", this.updatePos);
}
}
@Componnent
export default class MyComp extends Mixins<MouseMixin,>(MouseMixin,) {
// ...
// 直接读取x、y
}
在 jquery 中使用 vue
// a.js
import $ from "jquery";
import Vue from "vue";
import MyComp from "./Sf.vue";
const Sf = Vue.extend(MyComp);
let sf = new Sf({
// jq传参给vue组件
propsData: { age: 18 },
});
sf.$mount();
sf.$on('submit-xxx', () => {
// vue组件通过事件处理的方式实现与jq组件的通信
});
$("#myapp").append(sf);
// 当jquery组件被销毁时,记得也要把Vue组件销毁
function destroyJq() {
sf?.$destroy();
sf = null;
}
简单测试 js 代码执行耗时
const start = performance.now();
const arr = [];
for (let i = 0; i < 100000; i++) {
arr.push({});
}
const end = performance.now();
console.log(end - start); // 28.799
const _start = performance.now();
const _arr = [];
for (let i = 0; i < 100000; i++) {
_arr.push(Object.create(null));
}
const _end = performance.now();
console.log(_end - _start); // 101.5
参考
- MDN