赞
踩
跟 Node.js 一样,Deno 也是一个服务器运行时,但是支持多种语言,可以直接运行 JavaScript、TypeScript 和 WebAssembly 程序。
它内置了 V8 引擎,用来解释 JavaScript。同时,也内置了 tsc 引擎,解释 TypeScript。它使用 Rust 语言开发,由于 Rust 原生支持 WebAssembly,所以它也能直接运行 WebAssembly。它的异步操作不使用 libuv 这个库,而是使用 Rust 语言的 Tokio 库,来实现事件循环(event loop)。
Deno 只有一个可执行文件,所有操作都通过这个文件完成。它支持跨平台(Mac、Linux、Windows)。
Deno 具有安全控制,默认情况下脚本不具有读写权限。如果脚本未授权,就读写文件系统或网络,会报错。
必须使用参数,显式打开权限才可以。
Deno 支持 Web API,尽量跟浏览器保持一致。
它提供 window 这个全局对象,同时支持 fetch、webCrypto、worker 等 Web 标准,也支持 onload、onunload、addEventListener 等事件操作函数。
此外,Deno 所有的异步操作,一律返回 Promise。
Deno 只支持 ES 模块,跟浏览器的模块加载规则一致。没有 npm,没有 npm_modules 目录,没有require()命令(即不支持 CommonJS 模块),也不需要package.json文件。
所有模块通过 URL 加载,比如import { bar } from “https://foo.com/bar.ts”(绝对 URL)或import { bar } from ‘./foo/bar.ts’(相对 URL)。因此,Deno 不需要一个中心化的模块储存系统,可以从任何地方加载模块。
但是,Deno 下载模块以后,依然会有一个总的目录,在本地缓存模块,因此可以离线使用。
Deno 内置了开发者需要的各种功能,不再需要外部工具。打包、格式清理、测试、安装、文档生成、linting、脚本编译成可执行文件等,都有专门命令。
它建立了:
l Rust(Deno的核心是用Rust编写的,node用C++编写的)
l Tokio(以Rust编写的事件循环)
l TypeScript(Deno支持开箱即用的JavaScript和typeScript)
l V8(谷歌在Chrome和node中使用的JavaScript进行时)
物理CPU
物理CPU是相对于虚拟CPU而言的概念,指实际存在的CPU处理器,安装在PC主板或服务器上
物理核
CPU中包含的物理内核(核心)个数,比如多核CPU,单核CPU(古老的CPU)。这个多核或者单核已经集成在CPU内部了。
物理核数量 = cpu数(机子上装的cpu的数量) * 每个cpu的核心数
逻辑核(逻辑CPU或虚拟核)
所谓的4核8线程,4核指的是物理核心。用Intel的超线程技术(HT)将物理核虚拟而成的逻辑处理单元,现在大部分的主机的CPU都在使用HT技术,用一个物理核模拟两个虚拟核,即每个核两个线程,总数为8线程。
单核cpu和多核cpu
- 都是一个cpu,不同的是每个cpu上的核心数。
- 多核cpu是多个单核cpu的替代方案,多核cpu减小了体积,同时也减少了功耗。
- 一个核心只能同时执行一个线程。
串行
- 多个任务,执行时一个执行完再执行另一个。
- 比喻:吃完饭再看视频。
并发
- 多个线程在单个核心运行,同一时间一个线程运行,系统不停切换线程,看起来像同时运行,实际上是线程不停切换。
- 比喻: 一会跑去厨房吃饭,一会跑去客厅看视频。
并行
- 每个线程分配给独立的核心,线程同时运行。
- 比喻:一边吃饭一边看视频。
开个QQ,开了一个进程;开了迅雷,开了一个进程。在QQ的这个进程里,传输文字开一个线程、传输语音开了一个线程、弹出对话框又开了一个线程。所以运行某个软件,相当于开了一个进程。在这个软件运行的过程里(在这个进程里),多个工作支撑的完成QQ的运行,那么这“多个工作”分别有一个线程。所以一个进程管着多个线程。通俗的讲:“进程是爹妈,管着众多的线程儿子”…
进程就好比一个程序运行的实例,它是资源(CPU,内存等)分配的最小单位。计算机在同一时刻执行的进程不会超过CPU核心数,多进程能够的正常运行,靠的是CPU在进程间的快速切换。
线程是程序实例执行时的最小单位,是CPU调度和分派的基本单位。线程是程序的实际执行者,线程之间可以共享进程的资源。
联系:一个进程可以包含多个线程,但一个线程只能属于一个进程。
线程切换
- cpu给线程分配时间片(也就是分配给线程的时间),执行完时间片后会切换到另一个线程。
切换之前会保存线程的状态,下次时间片再给这个线程时才能知道当前状态。- 从保存线程A的状态再到切换到线程B时,重新加载线程B的状态的这个过程就叫上下文切换。而上下切换时会消耗大量的cpu时间。
• 进程是资源分配的最小单位,线程是cpu调度和分派的最小单位
• 进程有独立的地址空间,启动一个进程,就会单独为其分配地址空间,这种代价相对于线程很高;线程可以共享进程的资源,不需要单独申请地址空间。因此,CPU切换一个线程比进程花费小,同时创建一个线程也比进程小很多。
• 线程间通信更方便,共享进程的全局变量等;进程通信要通过IPC通信。
• 进程更健壮,多个进程有一个挂掉,不会影响正常运行;一个进程中多个线程,其中一个挂掉,整个进程就挂了。
【区别】:
调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位;
并发性:不仅进程之间可以并发执行,同一个进程的多个线程之间也可并发执行;
拥有资源:进程是拥有资源的一个独立单位,线程不拥有系统资源,但可以访问隶属于进程的资源。
进程所维护的是程序所包含的资源(静态资源), 如:地址空间,打开的文件句柄集,文件系统状态,信号处理handler等;线程所维护的运行相关的资源(动态资源),如:运行栈,调度相关的控制信息,待处理的信号集等;
系统开销:在创建或撤消进程时,由于系统都要为之分配和回收资源,导致系统的开销明显大于创建或撤消线程时的开销。但是进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。
线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个进程死掉就等于所有的线程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。
【联系】:
一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程;
资源分配给进程,同一进程的所有线程共享该进程的所有资源;
处理机分给线程,即真正在处理机上运行的是线程;
线程在执行过程中,需要协作同步。不同进程的线程间要利用消息通信的办法实现同步。
做个简单的比喻:进程=火车,线程=车厢
英文Coroutines,是一种比线程更加轻量级的存在。正如一个进程可以拥有多个线程一样,一个线程也可以拥有多个协程。
协程的特点在于是一个线程执行,那和多线程比,协程有何优势?
极高的执行效率:因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显;
不需要多线程的锁机制:因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。
class Publisher{ constructor(name){ this.name = name this.subsribers = [] this.state = 'XXXX' } // 被观察者要提供一个接受观察者的方法 subsribe(subsriber){ if (this.subsribers.indexOf(subsriber) === -1) { this.subsribers.push(subsriber) } } unsubsribe(subsriber){ if (this.subsribers.indexOf(subsriber) !== -1) { this.subsribers = this.subsribers.filter(sub => sub !== subsriber) } } // 改变被观察着的状态 publish(newState){ this.state = newState this.subsribers.forEach(sub =>{ sub.update(newState) }) } } class Subsriber{ constructor(name, cb){ this.name = name this.cb = cb } update(newState){ console.log(this.name); this.cb(newState) } } // 被观察者 灯 function func1(newState) { console.log(newState); } function func2(newState) { console.log(newState); } function func3(newState) { console.log(newState); } let pub = new Publisher('灯') let sub1 = new Subsriber('func1', func1) let sub2 = new Subsriber('func2',func2) let sub3 = new Subsriber('func3',func3) // 订阅/观察者 pub.subsribe(sub1) pub.subsribe(sub2) pub.unsubsribe(sub2) pub.subsribe(sub3) // 发布 pub.publish('hello world')
function Subscriber() { this.subs = []; // 存放订阅者回调函数 } Subscriber.prototype.subscribe = function (sub) { if(this.subs.indexOf(sub) === -1){ this.subs.push(sub); } }; Subscriber.prototype.unsubscribe = function(sub){ console.log("unsubscribe"); this.subs = this.subs.filter(s => s !== sub); }; Subscriber.prototype.publish = function () { console.log("publish"); this.subs.forEach(cb => { cb(); }); }; var subsriber = new Subscriber(); function func1() { console.log("func1"); } function func2() { console.log("func2"); } function func3() { console.log("func3"); } subsriber.subscribe(func1); subsriber.subscribe(func2); subsriber.subscribe(func2); subsriber.subscribe(func3); subsriber.unsubscribe(func2); subsriber.publish();
class EventEmitter { constructor() { this.events = {}; } on(event, callback) { let callbacks = this.events[event] || []; callbacks.push(callback); this.events[event] = callbacks; return this; } off(event, callback) { let callbacks = this.events[event]; this.events[event] = callbacks && callbacks.filter(fn => fn !== callback); return this; } emit(event, ...args) { let callbacks = this.events[event]; callbacks.forEach(fn => { fn(...args); }); return this; } once(event, callback) { let wrapFun = function (...args) { callback(...args); this.off(event, wrapFun); }; this.on(event, wrapFun); return this; } }
工厂模式是编程领域一种广为人知的设计模式,它抽象了创建具体对象的过程。JS 中创建一个函数,把创建新对象、添加对象属性、返回对象的过程放到这个函数中,用户只需调用函数来生成对象而无需关注对象创建细节,这叫工厂模式:
function createPerson(name, age) {
var person = new Object();
person.name = name;
person.age = age;
person.greeting = function() {
alert('Hi!');
};
return person;
}
var person1 = createPerson("leon", "20");
优缺点:
把所有依赖打包成一个 bundle.js 文件,通过代码分割成单元片段并按需加载。
用webpack优化前端性能是指优化webpack的输出结果,让打包的最终结果在浏览器运行快速高效。
UglifyJsPlugin
和ParallelUglifyPlugin
来压缩JS文件, 利用cssnano(css-loader?minimize)
来压缩css--optimize-minimize
来实现SplitChunksPlugin
插件来进⾏公共模块抽取,利⽤浏览器缓存可以⻓期缓存这些⽆需频繁变动的公共代码1.性能优化
2.减少浏览器向服务器的请求次数
3.节约服务器的带宽资源
Webpack是一个模块打包器,webpack可以递归的打包项目中的所有模块(递归:指定一个入口,分析模块的依赖,它会递归的查找所有相关的依赖),最终生成几个打包后的文件,他和其他的工具的最大的不同在于它支持code-splitting(代码分割),模块化(AMD,ESM,CommonJS)开发,全局的分析工具(分析整个项目引入的模块)。
webpack是基于入口的。webpack会自动地递归解析入口所需要加载的所有资源文件,然后用不同的Loader来处理不同的文件,用Plugin来扩展webpack功能。
webpack:
webpack是一个模块打包器,强调的是一个前端模块化方案,更侧重模块打包,我们可以把开发中的所有资源都看成是模块,通过loader和plugin对资源进行处理。,然后将所有这些模块打包成⼀个或多个 bundle。
Grunt、Gulp是基于任务运⾏的⼯具:
它们会⾃动执⾏指定的任务,就像流⽔线,把资源放上去然后通过不同插件进⾏加⼯,它们包含活跃的社区,丰富的插 件,能⽅便的打造各种⼯作流。
loader是用来告诉webpack如何转化处理某一类型的文件,并且引入到打包出的文件中
plugin是用来自定义webpack打包过程中的方式,一个插件是含有apply方法的一个对象,通过这个方法可以参与到整个webpack打包的各个流程(生命周期)
不同的作用
Loader直译为"加载器"。Webpack将一切文件视为模块,但是webpack原生是只能解析js文件,如果想将其他文件也打包的话,就会用到loader。 所以Loader的作用是让webpack拥有了加载和解析非JavaScript文件的能力。
Plugin直译为"插件"。Plugin可以扩展webpack的功能,让webpack具有更多的灵活性。 在 Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。
不同的用法
module.rules
中配置,也就是说他作为模块的解析规则而存在。 类型为数组,每一项都是一个Object
,里面描述了对于什么类型的文件(test
),使用什么加载器(loader
)和使用的参数(options
)plugins
中单独配置。 类型为数组,每一项是一个plugin
的实例,参数都通过构造函数传入。Loader像⼀个"翻译官"把读到的源⽂件内容转义成新的⽂件内容,并且每个Loader通过链式操作,将源⽂件⼀步步翻译成想要的样⼦。
编写Loader时要遵循单⼀原则,每个Loader只做⼀种"转义"⼯作。 每个Loader的拿到的是源⽂件内容( source ),可以通过返回值的⽅式将处理后的内容输出,也可以调⽤ this.callback() ⽅法,将内容返回给webpack。 还可以通过this.async() ⽣成⼀个 callback 函数,再⽤这个callback将处理后的内容输出出去。 此外 webpack 还为开发者准备了开发loader的⼯具函数集—— loader-utils 。
相对于Loader⽽⾔,Plugin的编写就灵活了许多。 webpack在运⾏的⽣命周期中会⼴播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。
webpack-dev-server使用内存来存储webpack开发环境下的打包文件,并且可以使用模块热更新,他比传统的http服务器对开发更加简单高效
webpack的热更新又称热替换(Hot Module Replacement),缩写为HMR。 这个机制可以做到不用刷新浏览器而将新变更的模块替换掉旧的模块。
大概流程是用webpack-dev-server启动一个服务之后,浏览器和服务端是通过websocket进行长连接,webpack内部实现的watch就会监听文件修改,只要有修改webpack就会重新打包编译到内存中,然后webpack-dev-server的依赖中间件webpack-dev-middleware和webpack之间进行交互,每次热更新都会请求一个携带hash值的json文件和一个js,websocket传递的也是hash值,内部机制通过hash值检查进行热更新。
loader: 'babel-loader?cacheDirectory=true'
plugins: [
new HappyPack({
id: 'happybabel',
loaders: ['babel-loader?cacheDirectory'],
// 开启 4 个线程
threads: 4
})
]
DllPlugin
DllPlugin 可以将特定的类库提前打包然后引入。这种方式可以极大的减少打包类库的次数,只有当类库更新版本才有需要重新打包,并且也实现了将公共代码抽离成单独文件的优化方案。
代码压缩
在 Webpack3 中,我们一般使用 UglifyJS 来压缩代码,但是这个是单线程运行的,为了加快效率,我们可以使用 webpack-parallel-uglify-plugin 来并行运行 UglifyJS,从而提高效率。
在 Webpack4 中,我们就不需要以上这些操作了,只需要将 mode 设置为 production 就可以默认开启以上功能。代码压缩也是我们必做的性能优化方案,当然我们不止可以压缩 JS 代码,还可以压缩 HTML、CSS 代码,并且在压缩 JS 代码的过程中,我们还可以通过配置实现比如删除 console.log 这类代码的功能。
CommonsChunkPlugin
来提取公共代码externals
配置来提取常用库DllPlugin
和DllReferencePlugin
预编译资源模块。通过DllPlugin
来对那些我们引用但是绝对不会修改的npm包来进行预编译,再通过DllReferencePlugin
将预编译的模块加载进来。Happypack
实现多线程加速编译webpack-uglify-parallel
来提升uglifyPlugin
的压缩速度。 原理上webpack-uglify-parallel
采用了多核并行压缩来提升压缩速度Tree-shaking
和Scope Hoisting
来剔除多余代码单页应用可以理解为webpack的标准模式,直接在entry中指定单页应用的入口即可,这里不再赘述
多页应用的话,可以使用webpack的 AutoWebPlugin来完成简单自动化的构建,但是前提是项目的目录结构必须遵守他预设的规范。
多页应用中要注意的是:
很多组件库已经提供了现成的解决方案,如Element出品的babel-plugin-component
和AntDesign出品的babel-plugin-import
安装以上插件后,在.babelrc
配置中或babel-loader
的参数中进行设置,即可实现组件按需加载了。
通过import(*)
语句来控制加载时机,webpack内置了对于import(*)
的解析,会将import(*)
中引入的模块作为一个新的入口,再生成一个chunk。 当代码执行到import(*)
语句时,会去加载Chunk对应生成的文件。import()
会返回一个Promise对象,所以为了让浏览器支持,需要事先注入Promise polyfill
主要还是为了减少页面的加载时间,将无用的代码删除,减少js包的大小,从而减少用户等待的时间,使用户不因为漫长的等待而离开。
那为什么已经有了dec,还要做tree shaking呢,根据作者的意思是,由于js静态语法分析的局限性,从已有代码里去删除代码不如去寻找真正使用的代码来的好。
具体还有哪些特性可以查一下文档。
tree shaking 首先会分析文件项目里具体哪些代码被引入了,哪些没有引入,然后将真正引入的代码打包进去,最后没有使用到的代码自然就不会存在了。
SPA(single page application) 单页面应用,是前后端分离时提出的一种解决方案。
优点:页面之间切换快;减少了服务器压力;
缺点:首屏打开速度慢,不利于 SEO 搜索引擎优。
SSR 的出现一定程度上解决了 SPA 首屏慢的问题,又极大的减少了普通 SPA 对于 SEO 的不利影响。
客户端渲染和服务器端渲染的最重要的区别就是究竟是谁来完成html文件的完整拼接,如果是在服务器端完成的,然后返回给客户端,就是服务器端渲染,而如果是前端做了更多的工作完成了html的拼接,则就是客户端渲染。
比如企业级网站,主要功能是展示而没有复杂的交互,并且需要良好的SEO,则这时我们就需要使用服务器端渲染;而类似后台管理页面,交互性比较强,不需要seo的考虑,那么就可以使用客户端渲染。
另外,具体使用何种渲染方法并不是绝对的,比如现在一些网站采用了首屏服务器端渲染,即对于用户最开始打开的那个页面采用的是服务器端渲染,这样就保证了渲染速度,而其他的页面采用客户端渲染,这样就完成了前后端分离。
axios是通过promise实现对ajax技术的一种封装,就像jQuery实现ajax封装一样。
简单来说: ajax技术实现了网页的局部数据刷新,axios实现了对ajax的封装。
axios是ajax,ajax不止axios。
Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。
特点:
- 从浏览器中创建 XMLHttpRequests
- 从 node.js 创建 http 请求
- 支持 Promise API
- 拦截请求和响应
- 转换请求数据和响应数据
- 取消请求
- 自动转换 JSON 数据
- 客户端支持防御 XSRF
AJAX
AJAX 是与服务器交换数据并更新部分网页的,在不重新加载整个页面的情况下。
Ajax = 异步 JavaScript 和 XML(标准通用标记语言的子集)。
// 拦截器
class Interceptor {
constructor() {
this.handlers = [];// 保存拦截器传入的回调
}
use(resolveHandler, rejectHandler) {
this.handlers.push({
resolveHandler,
rejectHandler
})
}
}
export default Interceptor
/* this代表axios实例指向 */
let promise = Promise.resolve(configs);
// 触发请求拦截器内的所有回调
this.interceptors.request.handlers.forEach(handler => {
promise = promise.then(handler.resolveHandler, handler.rejectHandler);
})
// 发请求
promise = promise.then(this.dispatch, undefined);
// 触发响应拦截器所有回调
this.interceptors.response.handlers.forEach(handler => {
promise = promise.then(handler.resolveHandler, handler.rejectHandler);
})
使用模块化可以给我们带来以下好处
在早期,使用立即执行函数实现模块化是常见的手段,通过函数作用域解决了命名冲突、污染全局作用域的问题
CommonJS 最早是 Node 在使用,目前也仍然广泛使用,比如在 Webpack 中你就能见到它,当然目前在 Node 中的模块管理已经和 CommonJS 有一些区别了。
exports 和 module.exports 用法相似,但是不能对 exports 直接赋值。因为 var exports = module.exports 这句代码表明了 exports 和 module.exports 享有相同地址,通过改变对象的属性值会对两者都起效,但是如果直接对 exports 赋值就会导致两者不再指向同一个内存地址,修改并不会对 module.exports 起效。
ES Module 是原生实现的模块化方案,与 CommonJS 有以下几个区别
ES6 Module和CommonJS模块的共同点:
优缺点
优缺点
优缺点
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype = {
constructor: Person,
nationality: "China",
showinfo: function () {
console.info(this.name);
}
}
export default Person;
上面代码中用对象字面量的形式重写了原型对象,这样相当于创建了一个新的对象,那么它的constructor属性就会指向Object,这里为了让它继续指向构造函数,显式的写上了constructor: Person。
这种构造函数与原型模式混成的模式,是目前在 JS 中使用最为广泛的一种创建对象的方法。
class 相对 function 是后出来的,既然 class 出来了,显然是为了解决 function 在处理面向对象设计中的缺陷而来。
class 作为 ES6 中的重大升级之一,有哪些优点呢?
要实现一个搜索框该怎么做,讨论了一下,首先是防抖\节流,然后是异步请求和页面数据显示的问题,面试官提示可能后一个请求会先于前面的请求返回。
然后写的时候想复杂了,卡了好一会儿,在面试官的指点下定了一个简单的方案,就是根据当前输入框中的数据与请求结果进行匹配,只显示当前输入框中的返回数据,其余返回结果都抛弃或是中断正在进行的请求等。
后端收到前端的这个搜索词条后,要进行模糊查找,不过现在有100W条数据(总之就是很多),要怎样高效的查找呢?然后这些查找到的数据假设也有很多,但是前端只需要显示那么几条数据,该怎么处理呢?
以上两种情况都会导致setTimeout的执行步调和屏幕的刷新步调不一致。
在setTimeout中对dom进行操作,必须要等到屏幕下次绘制时才能更新到屏幕上,如果两者步调不一致,就可能导致中间某一帧的操作被跨越过去,而直接更新下一帧的元素,从而导致丢帧现象。
与setTimeout相比,requestAnimationFrame最大的优势是由系统来决定回调函数的执行时机。
如果屏幕刷新率是60Hz,那么回调函数就每16.7ms被执行一次,如果刷新率是75Hz,那么这个时间间隔就变成了1000/75=13.3ms,换句话说就是,requestAnimationFrame的步伐跟着系统的刷新步伐走。它能保证回调函数在屏幕每一次的刷新间隔中只被执行一次,这样就不会引起丢帧现象。
DocumentFragment
,文档片段接口,表示一个没有父级文件的最小文档对象。它被作为一个轻量版的Document使用,用于存储已排好版的或尚未打理好格式的XML片段。最大的区别是因为DocumentFragment
不是真实DOM树的一部分,它的变化不会触发DOM树的(重新渲染) ,且不会导致性能等问题。
可以使用document.createDocumentFragment
方法或者构造函数来创建一个空的DocumentFragment
从MDN的说明中,我们得知DocumentFragments
是DOM节点,但并不是DOM树的一部分,可以认为是存在内存中的,所以将子元素插入到文档片段时不会引起页面回流。
当append元素到document中时,被append进去的元素的样式表的计算是同步发生的,此时调用 getComputedStyle
可以得到样式的计算值。
而append元素到documentFragment
中时,是不会计算元素的样式表,所以documentFragment
性能更优。
工作中,我们经常会遇到列表项。如果列表项的数量比较多,很多情况下我们会采用分页加载的方式,来避免一次性加载大量的数据,造成页面的性能问题。
但是用户在分页加载浏览了大量数据之后,列表项也会逐渐增多,此时页面可能会存在卡顿的情况。
虚拟列表是按需显示的一种技术,可以根据用户的滚动,不必渲染所有列表项,而只是渲染可视区域内的一部分列表元素的技术。
用数组保存所有列表元素的位置,只渲染可视区内的列表元素,当可视区滚动时,根据滚动的offset大小以及所有列表元素的位置,计算在可视区应该渲染哪些元素。
前端为什么要关注 Serverless? - 阿里巴巴淘系技术的回答 - 知乎
https://www.zhihu.com/question/378776917/answer/1326674674
Serverless 基本概念入门 - Serverless的文章 - 知乎
https://zhuanlan.zhihu.com/p/78250791
前端还没有工程化、模块化和可复用化的思维;
高内聚,低耦合;
工程化考量、项目管理的问题;
前端开发组件化/工程化框架;遵循一套体系来进行约束性开发;
包管理器:npm/yarn
用URL名词定位资源,用HTTP动词(GET,POST,DELETE,PUT)描述操作。
怎样用通俗的语言解释REST,以及RESTful? - 覃超的回答 - 知乎
https://www.zhihu.com/question/28557115/answer/48094438
看Url就知道要什么
看http method就知道干什么
看http status code就知道结果如何
资源压缩与合并:减少文件体积,提高加载速度
减少 http 请求
利用浏览器缓存:提升二次访问的速度
使用 CDN
利用浏览器缓存也仅仅只能提高二次访问的速度,对于首次访问的加速,可以从网络层面进行优化,最常见的手段就是 CDN(Content Delivery Network,内容分发网络)加速。
通过将静态资源缓存到离用户很近的相同网络运营商的 CDN 节点上,不但能提升用户的访问速度,还能节省服务器的带宽消耗。
CDN 是怎么做到加速的呢?
首先 CDN 服务商会在全国各个网站部署计算节点,CDN 加速将网站的内容缓存在网络边缘,不同地区的用户就会访问到离自己最近的相同网络线路上的 CDN 节点。当请求到达 CDN 节点之后,节点会判断自己的缓存内容是否有效,如果有效,就会立即响应缓存的内容给用户,从而加快响应速度。如果CDN节点的缓存失效,它会根据服务器的配置去我们的内容源服务器获取最先的资源响应给用户,并将内容缓存下来以便响应后续访问的用户。
因此,一个地区只要有一个用户先加载资源,在 CDN 中建立了缓存,该地区其他用户访问相同的资源的时候就可以使用缓存了。
CSS文件放在头部的原因是为了让用户第一时间看到的页面是有样式的。
另外,JS 文件也不是不可以放在头部,只要给script标签加上 defer 属性就可以异步加载,延迟执行了。
图片懒加载
减少重绘和重排
js style动画变成CSS transform动画
js动画为什么会有性能问题
那你知道为什么修改style会导致浏览器性能问题
cookie、localStorage、sessionStroage、indexDB
MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,是最常见的软件架构之一,业界有着广泛应用。用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。MVC被独特的发展起来用于映射传统的输入、处理和输出功能在一个逻辑的图形化用户界面的结构中。
Controller负责将Model的数据用View显示出来,换句话说就是在Controller里面把Model的数据赋值给View。
1)最上面的一层,是直接面向最终用户的"视图层"(View)。它是提供给用户的操作界面,是程序的外壳。
2)最底下的一层,是核心的"数据层"(Model),也就是程序需要操作的数据或信息。
3)中间的一层,就是"控制层"(Controller),它负责根据用户从"视图层"输入的指令,选取"数据层"中的数据,然后对其进行相应的操作,产生最终结果。
这三层是紧密联系在一起的,但又是互相独立的,每一层内部的变化不影响其他层。每一层都对外提供接口(Interface),供上面一层调用。这样一来,软件就可以实现模块化,修改外观或者变更数据都不用修改其他层,大大方便了维护和升级。
MVP 模式将 Controller 改名为 Presenter,同时改变了通信方向。
MVVM 模式将 Presenter 改名为 ViewModel,基本上与 MVP 模式完全一致。
唯一的区别是,它采用双向绑定(data-binding):View的变动,自动反映在 ViewModel,反之亦然。Angular 和 Ember 都采用这种模式。
响应式,双向数据绑定:
1.修改View层,Model对应数据发生变化。
2.Model数据变化,不需要查找DOM,直接更新View。
(1)发布者-订阅者模式: 一般通过sub, pub的方式实现数据和视图的绑定监听,更新数据方式通常做法是 vm.set(‘property’, value)。
(2)脏值检查: angular.js 是通过脏值检测的方式比对数据是否有变更,来决定是否更新视图,在指定的事件触发时进入脏值检测。
(3)数据劫持: vue.js 则是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。
Object.defineProperty()
Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
CDN的核心功能特写
CDN 的核心点有两个,一个是缓存,一个是回源。
这两个概念都非常好理解。对标到上面描述的过程,“缓存”就是说我们把资源 copy 一份到 CDN 服务器上这个过程,“回源”就是说 CDN 发现自己没有这个资源(一般是缓存的数据过期了),转头向根服务器(或者它的上层服务器)去要这个资源的过程。
回源是指浏览器在发送请求报文时,响应该请求报文的是源站点的服务器,而不是各节点上的缓存服务器(比如nginx开启缓存),那么这个过程相对于通过各节点上的缓存服务器来响应的话就称作为回源。回源的请求或流量太多的话,有可能会让源站点的服务器承载着过大的访问压力,进而影响服务的正常访问。
回源请求数比
统计数据来自所有边缘节点上的请求记录,其中,对于没有缓存或缓存过期(可缓存)的请求以及不可缓存的请求,均计入回源请求中,其他直接命中缓存的,则为命中请求。
回源流量比
回源流量是回源请求文件大小产生的流量和请求本身产生的流量 回源流量比=回源流量/回源流量+用户请求访问的流量
CDN是什么?使用CDN有什么优势? - 阿里巴巴淘系技术的回答 - 知乎
https://www.zhihu.com/question/36514327/answer/1604554133
(1)CDN的加速资源是跟域名绑定的。
(2)通过域名访问资源,首先是通过DNS分查找离用户最近的CDN节点(边缘服务器)的IP
(3)通过IP访问实际资源时,如果CDN上并没有缓存资源,则会到源站请求资源,并缓存到CDN节点上,这样,用户下一次访问时,该CDN节点就会有对应资源的缓存了。
CDN是什么?使用CDN有什么优势? - 阿里云云栖号的回答 - 知乎
https://www.zhihu.com/question/36514327/answer/193768864
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。