luoxue-victor / my-blog Goto Github PK
View Code? Open in Web Editor NEW公众号【前端技匠】作者
公众号【前端技匠】作者
script、image、iframe的src都不受同源策略的影响。
JSONP,回调函数+数据就是 JSON With Padding,简单、易部署。(做法:动态插入script标签,设置其src属性指向提供JSONP服务的URL地址,查询字符串中加入 callback 指定回调函数,返回的 JSON 被包裹在回调函数中以字符串的形式被返回,需将script标签插入body底部)。缺点是只支持GET,不支持POST(原因是通过地址栏传参所以只能使用GET)
document.domain 跨子域 ( 例如a.qq.com嵌套一个b.qq.com的iframe ,如果a.qq.com设置document.domain为qq.com 。b.qq.com设置document.domain为qq.com, 那么他俩就能互相通信了,不受跨域限制了。 注意:只能跨子域)
window.name + iframe ==> http://www.tuicool.com/articles/viMFbqV,支持跨主域。不支持POST
HTML5的postMessage()方法允许来自不同源的脚本采用异步方式进行有限的通信,可以实现跨文本档、多窗口、跨域消息传递。适用于不同窗口iframe之间的跨域
CORS(Cross Origin Resource Share)对方服务端设置响应头
服务端代理 在浏览器客户端不能跨域访问,而服务器端调用HTTP接口只是使用HTTP协议,不会执行JS脚本,不需要同源策略,也就没有跨越问题。简单地说,就是浏览器不能跨域,后台服务器可以跨域。(一种是通过http-proxy-middleware插件设置后端代理;另一种是通过使用http模块发出请求)
CORS请求默认不发送Cookie和HTTP认证信息。如果要把Cookie发到服务器,一方面要服务器同意,指定Access-Control-Allow-Credentials字段。
享元模式,运用共享技术,有效地支持大量的细粒度的对象,以避免对象之间拥有相同内容而造成多余的性能开销。
享元(flyweight)模式的主要作用:性能优化,当系统创建过多相似的对象而导致内存占用过高,可以采用这种设计模式进行优化。
享元模式将对象的属性区分为内部状态与外部状态,内部状态在创建的时候赋值,外部状态在实际需要用到的时候进行动态赋值
对于内部状态和外部状态的区分,有几点:
我们要创建 100 个大小相同颜色不同的 div。
interface Div {
width: number;
height: number;
color: string;
}
const divStore: Div[] = [];
class CreateDiv {
public width = 100;
public height = 100;
public color = this.randomColor()
// 随机颜色
private randomColor () {
const color = ['red', 'green', 'blue', 'white', 'black'];
return color[Math.floor(Math.random() * color.length)];
}
}
let count = 100;
while (count--) {
const innerDiv = new CreateDiv();
divStore.push(innerDiv);
}
const sizeof = require('object-sizeof')
console.log(sizeof(divStore)) // 5688
// 将 div 属性设置成内部跟外部两部分
interface Div {
outer: {
width: number;
height: number;
};
innter: {
color: string;
};
}
// 用来储存 Div
const divStore: Div[] = [];
// 创建外部 div 类
class CreateOuterDiv {
width: number = 100;
height: number = 100;
}
class CreateInnerDiv {
public color = this.randomColor()
// 随机颜色
private randomColor () {
const color = ['red', 'green', 'blue', 'white', 'black'];
return color[Math.floor(Math.random() * color.length)];
}
}
// 创建外部 div
const outerDiv = new CreateOuterDiv();
let innerDiv: number;
let count = 100;
while (count--) {
// 创建内部 div
innerDiv = new CreateInnerDiv();
divStore.push({
outer: outerDiv,
innter: innerDiv
});
}
const sizeof = require('object-sizeof')
// 因为这个方法会把引用的对象也全部算一遍,所以我们拆开来算
// 验证:100 * (innerDiv + outerDiv)= 5400 与上面算的 5688 很接近,可以认为这个方法是准确的
console.log(100 * (sizeof(innerDiv) + sizeof(outerDiv))) // 5400
// 100 * innerDiv + outerDiv = 1638
console.log(100 * sizeof(innerDiv) + sizeof(outerDiv)) // 1638
从上面的计算结果来看减少了很大的内存,因为 divStore 数组对象中 outerDiv 其实只有一个,都是它的引用而已。我们的内存占用是 100 * innerDiv + outerDiv,而不使用享元模式的空间是 100 * (innerDiv + outerDiv)
当从一个对象那里调取属性或方法时,如果该对象自身不存在这样的属性或方法,就会去自己关联的prototype对象那里寻找,如果prototype没有,就会去prototype关联的前辈prototype那里寻找,如果再没有则继续查找Prototype.Prototype引用的对象,依次类推,直到Prototype.….Prototype为undefined(Object的Prototype就是undefined)从而形成了所谓的“原型链”。
其中foo是Function对象的实例。而Function的原型对象同时又是Object的实例。这样就构成了一条原型链。
用来判断某个构造函数的prototype属性是否存在另外一个要检测对象的原型链上
对象的__proto__指向自己构造函数的prototype。obj.proto.proto...的原型链由此产生,包括我们的操作符instanceof正是通过探测obj.proto.proto... === Constructor.prototype来验证obj是否是Constructor的实例。
function C(){}
var o = new C(){}
//true 因为Object.getPrototypeOf(o) === C.prototype
o instanceof C
instanceof只能用来判断对象和函数,不能用来判断字符串和数字
用于测试一个对象是否存在于另一个对象的原型链上。
判断父级对象 可检查整个原型链
CDN做了两件事,一是让用户访问最近的节点,二是从缓存或者源站获取资源
CDN的工作原理:通过dns服务器来实现优质节点的选择,通过缓存来减少源站的压力。
HTML5 History两个新增的API:history.pushState 和 history.replaceState,两个 API 都会操作浏览器的历史记录,而不会引起页面的刷新。
Hash就是url 中看到 # ,我们需要一个根据监听哈希变化触发的事件( hashchange) 事件。我们用 window.location 处理哈希的改变时不会重新渲染页面,而是当作新页面加到历史记录中,这样我们跳转页面就可以在 hashchange 事件中注册 ajax 从而改变页面内容。 可以为hash的改变添加监听事件:
window.addEventListener("hashchange", funcRef, false)
从性能和用户体验的层面来比较的话,后端路由每次访问一个新页面的时候都要向服务器发送请求,然后服务器再响应请求,这个过程肯定会有延迟。而前端路由在访问一个新页面的时候仅仅是变换了一下路径而已,没有了网络延迟,对于用户体验来说会有相当大的提升。
前端路由的优点有很多,比如页面持久性,像大部分音乐网站,你都可以在播放歌曲的同时,跳转到别的页面而音乐没有中断,再比如前后端彻底分离。 开发一个前端路由,主要考虑到页面的可插拔、页面的生命周期、内存管理等。
使用浏览器的前进,后退键的时候会重新发送请求,没有合理地利用缓存。
History interface提供了两个新的方法:pushState(), replaceState()使得我们可以对浏览器历史记录栈进行修改:
window.history.pushState(stateObject, title, URL)
window.history.replaceState(stateObject, title, URL)
这个题目有很多人懂,但是又很多人不懂,这个专题就是让你彻底弄透 setInterval
创建一个空对象,并且this变量引用该对象,同时继承了该函数的原型(实例对象通过__proto__属性指向原型对象;obj.proto = Base.prototype;)
属性和方法被加入到 this 引用的对象中。
因为事件具有冒泡机制,因此我们可以利用冒泡的原理,把事件加到父级上,触发执行效果。这样做的好处当然就是提高性能了
定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行
我们明确地计划不同条件下创建不同实例时
优点
缺点
比如我要有一个 Animal
工厂,这个工厂要生产动物。那么我要定义动物都有 Feature
特征必须要有 color
颜色跟 bark
叫声。
// 定义工厂需要的动物特征
interface Feature {
color: string;
bark(): void;
}
// 定义动物类型名字
type name = 'cat' | 'dog'
// 子类必须要实现 Feature 接口的方法
// 这样我们就可以创建白色叫声喵喵喵的猫了
class Cat implements Feature {
color = "白色";
bark() {
console.log(`${this.color} 喵喵喵`);
}
}
// 创建 Dog 类
class Dog implements Feature {
color = "黑色";
bark() {
console.log(`${this.color} 汪汪汪`);
}
}
// 这就是一个动物工厂
class Animal {
createAnimal(type: name) {
switch (type) {
case 'cat':
return new Cat();
case 'dog':
return new Dog();
}
}
}
const animal = new Animal();
const cat = animal.createAnimal('cat');
const dog = animal.createAnimal('dog');
cat.bark()
dog.bark()
abstract class Feature {
abstract color: string;
abstract bark(): void;
}
// 枚举可以使用的动物类型
enum animalType {
'cat',
'dog'
}
// 子类继承抽象类 Feature
// 这样我们就可以创建白色叫声喵喵喵的猫了
class Cat extends Feature {
color = "白色";
bark() {
console.log(`${this.color} 喵喵喵`);
}
}
// 创建 Dog 类
class Dog extends Feature {
color = "黑色";
bark() {
console.log(`${this.color} 汪汪汪`);
}
}
// 这就是一个动物工厂
class Animal {
createAnimal(type: animalType) {
switch (type) {
case animalType.dog:
return new Cat();
case animalType.dog:
return new Dog();
}
}
}
const animal = new Animal();
const cat = animal.createAnimal(animalType.cat);
const dog = animal.createAnimal(animalType.dog);
cat.bark()
dog.bark()
设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。
如果我们把代码编程比作是战争的话,那么设计模式就是兵法。不会兵法肯定打不过会兵法的,即使能打过也要付出更多的代价。
设计模式,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。
目的:使用设计模式是为了可重用代码,让代码更容易被他人理解、保证代码的可靠性。
保证一个类仅有一个实例,并提供一个访问它的全局访问点
优点
减少内存开支
单例模式在内存中只有一个实例,减少内存开支,特别是一个对象需要频繁地创建销毁时,而且创建或销毁时性能又无法优化,单例模式就非常明显了减少性能开销
由于单例模式只生成一个实例,所以,减少系统的性能开销,当一个对象产生需要比较多的资源时,如读取配置,产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决。避免对资源的多重占用
例如一个写文件操作,由于只有一个实例存在内存中,避免对同一个资源文件的同时写操作设置全局的访问点
优化和共享资源访问,例如,可以设计一个单例类,负责所有数据表的映射处理。缺点
// 懒汉式,只有在调用 getInstance 的时候才会实例化 Singleton
class Singleton {
static instance = null;
// 获取实例方法
static getInstance() {
return this.instance || (this.instance = new Singleton());
}
}
const instance1 = Singleton.getInstance();
const instance2 = Singleton.getInstance();
console.log(instance1 == instance2); // true
// 饿汉式,在类初始化的时候就已经创建好了实例
class Singleton {
static instance = new Singleton();
// 获取实例方法
static getInstance() {
return this.instance;
}
}
const instance1 = Singleton.getInstance();
const instance2 = Singleton.getInstance();
console.log(instance1 == instance2); // true
HTTP/2采用二进制格式而非文本格式(二进制协议解析起来更高效)
HTTP/2是完全多路复用的,即一个TCP连接上同时跑多个HTTP请求
使用报头压缩,HTTP/2降低了开销
HTTP/2让服务器可以将响应主动“推送”到客户端缓存中,支持服务端推送(就是服务器可以对一个客户端请求发送多个响应)
HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,TLS/SSL中使用 了非对称加密,对称加密以及HASH算法。比http协议安全。
HTTPS 在传输数据之前需要客户端(浏览器)与服务端(网站)之间进行一次握手,在握手过程中将确立双方加密传输数据的密码信息
keep-alive使客户端到服务端的连接持久有效,当出现对服务器的后继请求时,keep-alive功能避免了建立或者重新连接
http1.0默认关闭,http1.1默认启用
优点:更高效,性能更高。因为避免了建立/释放连接的开销
缓存处理,在HTTP1.0中主要使用header里的If-Modified-Since,Expires来做为缓存判断的标准,HTTP1.1则引入更多缓存控制策略,例如Entity tag,If-Match,If-None-Match等
Http1.1支持长连接和请求的流水线(pipeline)处理,在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟,默认开启Connection:keep-alive
定义一系列的算法, 把它们一个个封装起来, 并且使它们可相互替换。
优点
缺点
在vue中有一个合并选项策略 optionMergeStrategies
,它的功能就是把选项添加一些策略,可以达到我们对选项数据操作的目的
官方例子,将选项 _my_option
添加策略,让它的值加一
Vue.config.optionMergeStrategies._my_option = function (parent, child, vm) {
return child + 1
}
const Profile = Vue.extend({
_my_option: 1
})
// Profile.options._my_option = 2
我们来简单实现一下这个合并选项策略
// 策略模式 store
const optionMergeStrategies: { [prop: string]: any } = {};
// 给 _my_option 添加策略
optionMergeStrategies._my_option = function(value) {
return value + 1
}
// 声明 data
const data = {
// 添加策略
_my_option: 1,
// 未添加策略
option: 1
};
// 响应式
function reactive (data) {
const hander = {
get(target, key, value) {
const v = Reflect.get(target, key, value);
// 此属性存在策略
if (typeof optionMergeStrategies[key] === 'function') {
return optionMergeStrategies[key](v)
}
return v
}
};
return new Proxy(data, hander);
}
const proxy = reactive(data);
// 测试是否添加了响应
proxy._my_option = 10
proxy.option = 10
console.log(proxy._my_option, proxy.option); // 11 10
这样你就可以做更多的事情了,比如验证手机号,邮箱等等,再也不用写很多的 if else 了,而且你也可以随时更换策略。符合了设计模式的开闭原则。
cookie优点:
cookie和session区别
sessionStorage是当前对话的缓存,浏览器窗口关闭即消失,localStorage持久存在,除非清除浏览器缓存。
页面缓存原理
页面缓存状态是由http header决定的,一个浏览器请求信息,一个是服务器响应信息。主要包括Pragma: no-cache、Cache-Control、 Expires、 Last-Modified、If-Modified-Since。
一是字面量重写原型会中断关系,使用引用类型的原型,并且子类型还无法给超类型传递参数。
借用构造函数虽然解决了刚才两种问题,但没有原型,则复用无从谈起。所以我们需要原型链+借用构造函数的模式,这种模式称为组合继承
组合式继承是比较常用的一种继承方法,其背后的思路是 使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。这样,既通过在原型上定义方法实现了函数复用,又保证每个实例都有它自己的属性。
For detail:https://segmentfault.com/a/1190000002440502
实际上浏览器输入 url 之后敲下回车就是先看本地 cache-control、expires 的情况,刷新(F5)就是忽略先看本地 cache-control、expires 的情况,带上条件 If-None-Match、If-Modified-Since,强制刷新(Ctrl + F5)就是不带条件的访问。
如果比较粗的说先后顺序应该是这样:
需要注意的是 如果同时有 etag 和 last-modified 存在,在发送请求的时候会一次性的发送给服务器,没有优先级,服务器会比较这两个信息.
如果expires和cache-control:max-age同时存在,expires会被cache-control 覆盖。
其中Expires和cache-control属于强缓存,last-modified和etag属于协商缓存 强缓存与协商缓存
区别:强缓存不发请求到服务器,协商缓存会发请求到服务器。
高内聚,低耦合,易读写,可复用
。高内聚
是指将在逻辑上可以归类为一个单元的代码封装在一起,尽量保障一块代码集合主要解决一个需求,在前端开发中,最常见的便是将一个逻辑单元的代码使用IIFE函数进行封装。
低耦合
可以说,保障代码高内聚即在一定程度上满足了代码“低耦合”的要求,因为低耦合即是要求一个逻辑单元的代码块在改动时,不会造成其他逻辑单元代码块的变动,在前端开发中,即是要求各代码块不要过多共享某变量或对象,在不得以的情况下,一定要清晰注释。
易读写
是指保持代码的可读,可修改性,即在一定时期后,后来的开发者依然可以凭借阅读代码的方式,了解你的编程思路,并根据新的需求,修改你留下的代码。这里牵扯到良好的代码缩进,命名规范,注释等。
可复用
是指处于节省程序员生命的目的,遵从DRY原则,在一个项目中,抽象出功能类似的业务需求,通过组件化的方式编写代码,实现只写一次,到处使用的令人愉快的目的。
你是如何看待组件的?如果让你写一个组件你会考虑哪些?
作用域链的作用是保证执行环境里有权访问的变量和函数是有序的,作用域链的变量只能向上访问,变量访问到window对象即被终止,作用域链向下访问变量是不被允许的。
装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。
这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。
优点
缺点
比如我们要点击一个按钮,但是这个按钮点击时我们想给他加上埋点并做一些登陆的逻辑
我这里使用了 es7
的语法糖,当然不用语法糖也可以做,但是我觉得用的话更简洁一些
// Button 类,内部有一个 click 方法
// 对click方法做了两个修饰
// 一个是添加埋点,一个是登陆
class Button {
@BuridDecorator
@LoginDecorator
click() {
console.log('点击 dom')
}
}
// 登陆逻辑的装饰器
function LoginDecorator(target, name, descriptor) {
const oriFun = target[name]
descriptor.value = async function() {
const code = await Login();
if (code === 0) {
console.log('登陆成功')
oriFun.call(this, ...arguments)
}
}
}
// 设置埋点的装饰器
function BuridDecorator(target, name, descriptor) {
console.log(`${name} 方法添加了一个埋点`)
}
// 登陆逻辑
async function Login () {
return new Promise((resolve, reject)=> {
setTimeout(() => {
resolve(0)
}, 1000)
})
}
// 点击按钮
const btn = new Button()
btn.click();
// click 方法添加了一个埋点
// 登陆成功
// 点击 dom
为了准确无误地把数据送达目标处,TCP协议采用了三次握手策略。用TCP协议把数据包送出去后,TCP不会对传送 后的情况置之不理,它一定会向对方确认是否成功送达。握手过程中使用了TCP的标志:SYN和ACK。
发送端首先发送一个带SYN标志的数据包给对方。接收端收到后,回传一个带有SYN/ACK标志的数据包以示传达确认信息。 最后,发送端再回传一个带ACK标志的数据包,代表“握手”结束。 若在握手过程中某个阶段莫名中断,TCP协议会再次以相同的顺序发送相同的数据包。
适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。
优点
缺点
举一个例子,你要写一个页面兼容各个端的小程序,那么你就需要根据环境调用不同小程序的 sdk 方法。比如在支付宝中有一个 zhifubaoShare
的分享方法,在微信中有一个 weixinShare
的分享方法。(当然一个sdk还有很多方法,我们只拿分享来举例子)但是我们在工作中其实只希望调用一个 share
方法就能实现不同端的分享。下面我们用适配器模式来做一个 Adapter
适配器。
// =============== 定义接口与类型 ==============================
// 支付宝接口
interface ZhifubaoInerface {
zhifubaoShare(): void;
}
// 微信接口
interface WeixinInterface {
weixinShare(): void;
}
// adapter 接口
interface AdapterInterface {
share(): void;
}
// 合并所有 sdk 类型
interface MergeSdk extends ZhifubaoInerface, WeixinInterface {}
// 支持的平台类型
type platform = 'weixin' | 'zhifubao';
// =============== 代码逻辑实现 ==============================
// 微信 sdk 类实现
class WeixinSdk implements WeixinInterface {
weixinShare() {
console.log('微信分享');
}
}
// 支付宝 sdk 类实现
class ZhifubaoSdk implements ZhifubaoInerface {
zhifubaoShare() {
console.log('支付宝分享');
}
}
// adapter 类实现
class Adapter implements AdapterInterface {
constructor() {
this.sdk = this.getPlatfromSdk();
}
// 挂载 sdk
private sdk: MergeSdk;
// 根据 ua 获取到平台
private getPlatform(): platform {
// 默认写了 weixin
return 'weixin';
}
// 将所有 sdk 方法放进一个 map 里
private getPlatfromSdk() {
const map = {
weixin: WeixinSdk,
zhifubao: ZhifubaoSdk
};
const platform = this.getPlatform();
return new map[platform]() as MergeSdk;
}
// 分享功能
// 实际项目中还有参数的问题,这里为了代码的简洁就不写了
public share() {
const platform = this.getPlatform();
switch (platform) {
case 'weixin':
this.sdk.weixinShare();
break;
case 'zhifubao':
this.sdk.zhifubaoShare();
break;
default:
console.log('此方法不存在');
}
}
}
const adapter = new Adapter();
// 因为我们默认设置了 weixin 平台
adapter.share(); // 微信分享
只有 UI控件 才存在双向,非 UI控件 只有单向。 单向绑定的优点是可以带来单向数据流,这样的好处是流动方向可以跟踪,流动单一,没有状态, 这使得单向绑定能够避免状态管理在复杂度上升时产生的各种问题, 程序的调试会变得相对容易。单向数据流更利于状态的维护及优化,更利于组件之间的通信,更利于组件的复用
无需进行和单向数据绑定的那些CRUD(Create,Retrieve,Update,Delete)操作; 双向绑定在一些需要实时反应用户输入的场合会非常方便 用户在视图上的修改会自动同步到数据模型中去,数据模型中值的变化也会立刻同步到视图中去;
双向数据流是自动管理状态的, 但是在实际应用中会有很多不得不手动处理状态变化的逻辑, 使得程序复杂度上升 无法追踪局部状态的变化 双向数据流,值和UI绑定,但由于各种数据相互依赖相互绑定,导致数据问题的源头难以被跟踪到
Vue 虽然通过 v-model 支持双向绑定,但是如果引入了类似redux的vuex,就无法同时使用 v-model。
双绑跟单向绑定之间的差异只在于,双向绑定把数据变更的操作隐藏在框架内部,调用者并不会直接感知。
<input v-model="something">
<!-- 等价于以下内容 -->
<input :value="something" @input="something = $event.target.value">
也就是说,你只需要在组件中声明一个name为value的props,并且通过触发input事件传入一个值,就能修改这个value。
开闭原则的意思是:对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。简言之,是为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。
里氏代换原则是面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。LSP 是继承复用的基石,只有当派生类可以替换掉基类,且软件单位的功能不受到影响时,基类才能真正被复用,而派生类也能够在基类的基础上增加新的行为。里氏代换原则是对开闭原则的补充。实现开闭原则的关键步骤就是抽象化,而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。
这个原则是开闭原则的基础,具体内容:针对接口编程,依赖于抽象而不依赖于具体。
这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。它还有另外一个意思是:降低类之间的耦合度。由此可见,其实设计模式就是从大型软件架构出发、便于升级和维护的软件设计**,它强调降低依赖,降低耦合。
最少知道原则是指:一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立。
合成复用原则是指:尽量使用合成/聚合的方式,而不是使用继承。
前端工程化不外乎两点,规范和自动化。
包括 团队开发规范,模块化开发,组件化开发,组件仓库,性能优化,部署,测试,开发流程,开发工具,脚手架,git工作流,团队协作
1.构建工具 2.持续集成 3.系统测试 4.日志统计 5.上线部署 6.敏捷开发 7.性能优化 8.基础框架
正向代理: 用浏览器访问时,被残忍的block,于是你可以在国外搭建一台代理服务器,让代理帮我去请求google.com,代理把请求返回的相应结构再返回给我。
反向代理:反向代理服务器会帮我们把请求转发到真实的服务器那里去。Nginx就是性能非常好的反向代理服务器,用来做负载均衡。 正向代理的对象是客户端,反向代理的对象是服务端
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.