Minimal and Clean blog theme for Hugo
授课项目首屏加载慢
现象
epp项目在pad端加载需要10-30s时间。
为什么会这样?
pad端是RN项目,前面2个界面的操作之后开启webview访问学生端网址,加载epp项目。
观察epp学生端项目的加载:
![优化前][优化-前.jpg]
这个是渲染第一帧前加载资源的截图,可以看到有17个js文件,而很明显的,fabric、ckeditor、aliplayer这些体积很大的库在学生端是不需要的(这3个文件都是编辑器用的,epp项目分学生端、教师端、编辑器),而且资源数量较多。
两个原因导致了pad端需要加载10s的问题:资源数量多、很多不需要的代码被加载。
30s的情况出现在网络差的时候,比如上课时,有30个学生同时使用pad,瓜分宽带导致资源下载速度更慢。这个后面也有方案尝试解决。
问题处在哪?
从代码的入口文件看起,发现了这段代码:
![懒加载][优化-代码.png]
这段代码的作用是根据url来加载不同的场景(学生端、教师端、编辑器),重点是下面这段代码。
const importScene = () =>
import(`@/scene/${sceneName}Scene/${sceneName}Scene`)
.then(module => module.default);
使用webpack的代码分割、懒加载机制。webpack会将import("./module")
作为分割点,将其放在单独的chunk文件中,实现懒加载。
import()
也可以接受动态表达式,比如这样import(./routes/${path}/route)
,此时import()
为每一个可能的模块创建独立的chunk,这句话的重点是可能的模块,也就是上面代码的问题所在。
对于前面那段代码,import的动态的部分就是sceneName,对于webpack来说它是一个未知变量,所以webpack认为它是可能的模块。
在我们的业务中,可能的模块包含来学生端、教室端、编辑器,那么webpack会将3个大模块全部分别打包,而且将其全部下载。从而导致来前面提到的2个问题。
解决问题
尝试着把上面提到的那段动态表达式改成确定的语句,比如:
const importStudentScene = () =>
import('@/scene/studentScene/studentScene')
.then(module => module.default);
const app = edApplication.getInstance('edinnova');
importStudentScene().then(studentScene => {
app.addScene(new studentScene());
app.startScene('studentScene', '#app');
});
再次开启项目:
![优化后][优化-后.jpg]
可以看到数量和体积都小了很多。
![优化后][优化-后2.jpg]
这是在电脑上的截图,优化后首屏从3.4s变为2.1s,效果明显。这只是一个几乎没有改动代码的一步优化。
可以看到优化后还是有1个600k,1个200k两个chunk文件,后面还需要分析这两个chunk文件,看是否有优化空间。
30个学生同时使用pad
有一个猜想,因为30个学生是在同一个教室的局域网内。考虑使用webRTC的种子下载技术,实现下载速度的优化。
…electron
基于 electronforge 开发
npm init electron-app@latest my-app -- --template=vite
不能用 pnpm 因为软连接导致 make 报错了
…怎么写简历
《程序员面试金典》摘录
- 篇幅较短的简历通常会令人印象更为深刻
- 聘人员浏览一份简历一般只会用10秒钟左右
- 建议工作经验不足10年的求职者将简历压缩成1页
工作经历
简历不是也不应该是工作经历的编年史。应该只列举那些相关的工作经验——那些会给别人留下深刻印象的工作经验。
在描述工作经历时,请尽量采用这样的格式:“使用Y实现了X,从而达到了Z效果。”比如下面这个例子:
❑ “通过实施分布式缓存功能减少了75% 的对象渲染时间,从而使得用户登录速度加快了10%。” 下面还有一个例子,描述略有不同:
❑ “实现了一种新的基于windiff的比较算法,系统平均匹配精度由1.2提升至1.5。”
尽管不是所有经历都能套用此句型,但原则无非是描述做过什么,如何完成,结果如何。理想的做法是尽可能地量化结果。
项目经历
简历上应该只列举2到4个最重要的项目。描述项目要简明扼要,比如使用哪些语言或技术。你也可以加上一些细节,比如该项目是个人独立开发还是团队合作的成果,是某一门课程的一部分还是独立开发的。当然,除非能让简历更出彩,否则这些细节不一定放到简历上。独立项目一般说来比课程设计会更加出彩,因为这些项目会展现出你的主动性。
项目也不要列太多。很多求职者都犯过这样的错误,在简历上一股脑儿列出先前做过的13个项目,鱼龙混杂,效果反而不佳。
那么,应该列出哪些项目呢?说实在的,其实这并没有那么重要。有一些公司非常喜欢开源项目(参与这些项目说明具备了大型代码库开发的经验),另一些公司则更喜欢独立项目(了解你在这些项目中的贡献会更加容易)。你的项目可以是一款移动应用、网络应用或者任何东西。最重要的是,你确实参与了开发。
编程语言
一种策略是列出你用过的主要语言,后面加上熟练程度,比如像下面这样的。
❑ 编程语言:Java(非常熟练),C++(熟练),JavaScript(有过使用经验)。
面试准备清单
逐字逐句检查简历,确保回答每个部分或项目时都能对答如流。填写下面的表格,它会助你一臂之力。
可以在表头中列出在简历中提到的主要事项,比如项目、职位或活动。然后在每一行写清楚常见问题。
在面试前温习这个表格。为了方便掌握和记忆,可以把每个故事提炼为几个关键词。这样,就可以在面试时胸有成竹、从容不迫了。
另外,确保你有1至3个项目可以拿得出手,并能就其细节侃侃而谈。你应该是这些项目的主力,并且有能力同面试官深入探讨相关的技术细节。
你有哪些缺点
你应该提到真实、合乎情理的缺点,然后话锋一转,强调自己是如何克服这个缺点的。
举例如下。
“有时候,我对细节不够重视。好的一面是我反应迅速,执行力强,但不免会因为粗心大意而犯错。有鉴于此,我总是会找其他同事帮忙检查自己的工作,确保不出问题。”
掘金
工作经历&项目经历
加分写法:
- 工作经历项目经历可参照万能的STAR法则来写,STAR不清楚的童鞋点这里啦
- 效力过哪些公司,我们匹配的公司? BAT? 知名大型互联网公司?
- 做过什么行业领域,和我们目前的行业是否匹配
- 擅长的技术语言,应用了哪些技术栈,(Java, Scala,Ruby, React, Vue, Microservice…)
- 经历的项目复杂度,及在项目中承担什么样的角色(人的变化/技术的变化/环境的变化/不同工作经历相同角色的不同点)
- 时间节点(空档期)
栗子2正确打开方式:
西安XXX公司 Java工程师 — 2016.2月-2017.2月
1、MOGU推荐架构数据与缓存层设计开发
- MOGU是一款时尚资讯app,负责推荐页面资讯feed流的展示及用户历史的展示
- 负责数据层,处理前端逻辑整个开发工作,分布式rpc服务搭建
- 负责进行压测监测、缓存处理,对接又进行改进优化,主用redis缓存
2、基于JAVA的电商爬虫开发
- 使用java搭建爬虫server平台,进行配置和开发,进行网页改版监测功能开发
- 爬取淘宝时尚品牌与其他电商网站商品品牌与详情等
- 通过频率、ip池、匿名代理等应对一些网站的反爬
3、同图搜索Solr服务开发
基于算法组的同图策略,使用solr做java接又实现rpc服务搭建,进行索引构建和solr实现
北京XXX
java大数据工程师— 2013.4月-2015.12月
1、负责实时流消息处理应用系统构建和实现
- 在调研了kafka的优势和我们的具体需求之后,用kafka作为消费者,保证高吞吐处理消息,并持久化消息的同时供其它服务使用,进行了系统的设计和搭建使用。 本地日志保证消息不丢失,并通过记录游标滑动重复读取数据。
- 使用storm 负责搭建消息处理架构,并完成基于业务的消息落地,提供后续的数据 统计分析实时和离线任务,诸如pv、uv等数据,为运营做决策
- 网站用户行为埋点和基于js的日志收集器开发,定义接又和前端部门配合。主用go 2、hadoop集群搭建和数据分析处理
2、基于CDH的集群搭建工作,后期进行维护
…gitlab-ci
需求
一个前端项目需要分别发布到线上的测试环境和生产环境。在打包步骤,利用CI的环境变量功能,分别配置不同的API地址、项目地址等。完成打包后,将其上传到阿里oss中,完成发布。
使用Gitlab CI/CD的准备工作
需要准备三个东西:
.gitlab-ci.yml
文件定义CI的工作流程、- Runner执行流程、
- 阿里云OSS的SDK及上传程序用于部署。
.gitlab-ci.yml文件
项目根目录下需要有一个 .gitlab-ci.yml
文件。
声明流程
stages:
- install
- build
stages
声明流程,这里就是包括 install
和 build
两个流程。这两个流程都做哪些工作呢?这里的声明相当于一个类型或者接口,需要在后面去实现它。
实现流程
job_name:
stage: install
script:
- npm install
这个 job
就是实现 stages
中声明的 install
流程的,其中 job_name
随便写就是个名字,stage
字段是说这个 job
是实现的哪个接口。一个接口也可以被实现多次,但是要有条件区分开。
build_develop:
stage: build
variables:
REACT_APP_BASE_UR: https://develop.edinnovaedu.com:35040/
REACT_APP_LEARNBOT_URL: https://ng-api--dev.edinnovaedu.com/
only:
- develop
script:
- npm run build:dev
- npm run deploy
build_production:
stage: build
variables:
REACT_APP_BASE_URL: https://api-gray.v.edinnovaedu.com/
REACT_APP_LEARNBOT_URL: https://api-gray.v.edinnovaedu.com
only:
- master
script:
- npm run build:gray
- npm run deploy
上面的两个 job
,都是 build
的实现,它们根据 only
字段定义的条件(分支名)做不同的工作。
CSS3
常用的css3属性
- 圆角 border-radius
- 阴影 box-shadow
- 透明度 opacity
- 渐变色 gradient
- 旋转 rotate
- transform
- scale
- transitions
- animation
- keyframe
跨域
什么是跨域问题
跨域问题源于浏览器的同源策略
在网页中使用AJAX时,AJAX请求的URL与网页的URL相比较,如果是属于同一个域名才可以正常访问,如果不属于一个域名就会出现跨域问题。所以,怎么判断两个URL是不是属于同一个域名呢?
比如一个网页的URL是"https://segmentfault.com/a/1190000011145364?aaa=666",可以把它拆分开:
- origin: “https://segmentfault.com”
- pathname: “/a/1190000011145364”
- search: “?aaa=666”
只有当两个URL的origin完全相同时,才不会出现跨域问题。所以以下origin都会出现跨域问题:
- 子域名:https://abc.segmentfault.com
- 不同协议:http://segmentfault.com
- 不同端口:https://segmentfault.com:8080
- IP地址:https://192.168.4.12
当出现跨域问题时,AJAX请求返回后浏览器控制台会报错,AJAX的回调拿不到返回值。
CORS
cross-origin-resouces-shared
这个方案好像不需要在JS代码中的额外操作。另外,默认情况下,Cookie不包括在CORS请求之中,如果需要请求中带着cookie,设置withCredentials参数即可。
使用 fetch 进行请求时,可在参数中加 mode : 'no-cors'
,使浏览器不发送 option 请求,直接发起网络请求。但是请求的 header 只能是 CORS-safelisted 列表中的。其他 header 浏览器不会发送。比如 Authorization
no-cors
对 header 有影响,
需要在服务器为response设置header
- origin允许的域名
- methods允许的http方法
- credentials允许携带cookies
- headers循序的请求头
用 fetch 发起 cors 请求
fetch('https://english-workers.407590300.workers.dev/corsproxy/login', {
credentials: 'include',
// mode: 'cors',
// method:"GET",
headers: new Headers({
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'X-Custom-PSK': 'mypresharedkey1',
})
})
JSPNP
前端发起请求,并带上回调函数
<script src="https://anotherweb.com/api/tourism-data.json?myCallback=tourismJSONP"></script>
服务器返回的
tourismJSONP({"city":"Barcelona"})
处理返回结果
function tourismJSONP(data){
alert(data.city); // "Barcelona"
}
利用script标签不受跨域限制
…==类型转换
==类型转换
- 两个类型不同时会发生转换
- 一个是布尔,则将布尔转为数字,false-0,true-1
- 一个是字符串,另一个是数字,则将字符创转为数值
- 一个是对象,另一个不是,则调用对象的valueOf方法转为基本类型
- null与undefined相同
- 一个是NaN,则返回false(不等比较返回true)(两个NaN不相等)
- 两个都是对象,则比较他们是不是同一地址
JavaScript基础
原型
- 是什么 它是所有对象的一个非标准属性,但是所有浏览器都实现了它, 它用于获取原型链的属性和方法, 在es6中标准化为Object.getPrototypeOf(obj)方法
它和prototype的区别是,只有函数对象才有prototype属性,用于记录原型属性和方法。
他们的关系是 对象.ptoto === 对象构造器.protptype
它主要是用来实现js中的继承
词法作用域
也叫静态作用域,是在写代码的地方确定的作用域。
为什么Map的性能比对象好?
…new操作符
当使用 new
关键字调用函数时,该函数将被用作构造函数。new
的执行过程是:
- 创建一个空的简单 JavaScript 对象。为方便起见,我们称之为
newInstance
。 - 如果构造函数的
prototype
属性是一个对象,则将newInstance
的[[Prototype]]
指向构造函数的prototype
属性,否则newInstance
将保持为一个普通对象,其[[Prototype]]
为Object.prototype
。 - 使用给定参数执行构造函数,并将
newInstance
绑定为this
的上下文(换句话说,在构造函数中的所有this
引用都指向newInstance
)。 - 如果构造函数返回非原始值,则该返回值成为整个
new
表达式的结果。否则,如果构造函数未返回任何值或返回了一个原始值,则返回newInstance
。(通常构造函数不返回值,但可以选择返回值,以覆盖正常的对象创建过程。)
根据 MDN 中对 new
操作符的描述,我们可以实现一个名为 myNew
的函数来模拟 new
操作符的行为。下面是一个简单的实现:
function myNew(constructor, ...args) {
// 步骤 1:创建一个空的简单 JavaScript 对象
const newInstance = {};
// 步骤 2:将 newInstance 的 [[Prototype]] 指向构造函数的 prototype 属性
if (constructor.prototype !== null && typeof constructor.prototype === 'object') {
Object.setPrototypeOf(newInstance, constructor.prototype);
}
// 步骤 3:使用给定参数执行构造函数,并将 newInstance 绑定为 this 的上下文
const result = constructor.apply(newInstance, args);
// 步骤 4:如果构造函数返回非原始值,则返回该值;否则返回 newInstance
return typeof result === 'object' && result !== null ? result : newInstance;
}
使用这个 myNew
函数可以模拟 new
操作符的行为。例如:
Webpack
Loader 和 Plugin
Webpack 中的 Loader 和 Plugin 是两个不同的概念,它们分别用于处理不同的任务:
-
Loader:
- Loader 用于对模块的源代码进行转换和处理,通常用于加载和转换各种类型的文件,比如将 ES6/ES7 代码转换为 ES5、将 SCSS 文件转换为 CSS 等。
- Loader 是一个函数或者一个模块,它接受源文件作为输入,并返回转换后的文件内容。
-
Plugin:
- Plugin 用于扩展 Webpack 的功能,在构建过程中执行各种任务,比如打包优化、资源管理、环境变量注入等。
- Plugin 是一个具有 apply 方法的 JavaScript 对象,它会在 Webpack 的不同生命周期中被调用,并可以访问到整个编译过程中的各种信息和资源。
下面分别展示如何实现一个自定义的 Loader 和 Plugin:
自定义 Loader 示例:
假设我们要实现一个简单的 Loader,将源代码中的每个单词首字母大写。首先创建一个 capitalize-loader.js
文件:
module.exports = function(source) {
// 将源代码中每个单词的首字母大写
return source.replace(/\b\w/g, function(match) {
return match.toUpperCase();
});
};
然后在 Webpack 配置文件中配置该 Loader:
module: {
rules: [
{
test: /\.txt$/, // 匹配 .txt 文件
use: 'capitalize-loader' // 使用 capitalize-loader 处理 .txt 文件
}
]
}
现在,当 Webpack 在处理 .txt
文件时,会自动使用我们定义的 capitalize-loader
来处理文件内容。