ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)
本质上,这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值
如果解构不成功,变量的值就等于undefined
解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。由于undefined和null无法转为对象,所以对它们进行解构赋值,都会报错、
交换变量的值
例如:let x=1,y=2;[x,y] = [y,x]
从函数返回多个值
函数只能返回一个值,如果要返回多个值,只能将它们放在数组或对象里返回。有了解构赋值,取出这些值就非常方便
函数参数的定义
解构赋值可以方便地将一组参数与变量名对应起来
提取 JSON 数据,很多接口数据只需要其中某部分
例如aa.axios.get(res=>{let {data:result}=res;}),则res.data.result = result了
函数参数的默认值
指定参数的默认值,就避免了在函数体内部再写var foo = config.foo || ‘default foo’;这样的语句
遍历 Map 结构
Map 结构原生支持 Iterator 接口,配合变量的解构赋值,获取键名和键值就非常方便
输入模块的指定方法
加载模块时,往往需要指定输入哪些方法。解构赋值使得输入语句非常清晰。* const { SourceMapConsumer, SourceNode } = require(“source-map”);
左右两侧数据解构须得吻合,或者等号左边的模式,只匹配一部分的等号右边的数组(属于不完全解构)
特殊情况使用…扩展运算符,无值是空数组
左右两边等式的性质要相同,等号的右边不是数组(或者严格地说,不是可遍历的结构),那么将会报错,只要某种数据结构具有 Iterator
接口,都可以采用数组形式的解构赋值,例如Set结构
解构赋值允许指定默认值,当一个数组成员严格等于undefined,默认值才会生效,否则取赋值的值;如果默认值是一个表达式,那么这个表达式是惰性求值的,即只有在用到的时候,才会求值;默认值可以引用解构赋值的其他变量,但该变量必须已经声明
// 数组的解构赋值 let [a,b] = [1,2]; console.log([a,b],a);//[1, 2] 1 let [aa] = [11,22]; console.log(aa)//11 let [aaa,bbb] = [111]; console.log(aaa,bbb)//111 undefined let [head, ...tail] = [1, 2, 3, 4]; console.log(head,tail)//1,[2,3,4] let [x, y, ...z] = ['a']; console.log(x,y,z)//a undefined [] // 等号右边不是数组会报错 // let [ab] = 121; // conosle.log(ab)//TypeError: 121 is not iterable // let [abc] = {} // conosle.log(abc)//TypeError: {} is not iterable // 默认值赋值 let [zz = 1] = [undefined]; console.log(zz)//1 let [zzz = 1] = [null]; console.log(zzz)//null let [foo = true] = []; console.log(foo)// true let [xxx, yyy = 'b'] = ['a']; console.log(xxx,yyy)//a,b let [xxxx, yyyy = 'b'] = ['a', undefined]; console.log(xxxx,yyyy)//a,b function f() { console.log('aaa'); } let [xx = f()] = [1]; console.log(xx)//1 let [qq=ww,ww=11] = [23,44]; console.log(qq,ww)//23,44,因为ww申明比qq晚所以是undefined;
2、对象的解构赋值
对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者
数组是按照位置区分,对象则是按照键名区分的,同样的解构失败则为undefine
可将已有方法对象解构赋值
嵌套赋值,注意是变量是否被赋值是模式还是键值
对象的解构赋值可以取到继承的属性
如果要将一个已经声明的变量用于解构赋值,必须非常小心
let xx; // {xx} = {xx: 1}这样会报错,
解构赋值允许等号左边的模式之中,不放置任何变量名。因此,可以写出非常古怪的赋值表达式
({} = [true, false]);//可执行
由于数组本质是特殊的对象,因此可以对数组进行对象属性的解构
objFuc(){ // 对象解构赋值 let {b,a} = {a:1} console.log(a,b)//1 undefined // 已有对象解构赋值 let { sin, cos } = Math;//将Math对象的对数、正弦、余弦三个方法,赋值到对应的变量上 console.log(sin);//log() { [native code] } const { log } = console; log('hello') // hello // let { foo: baz } = { foo: 'aaa', bar: 'bbb' }; console.log(baz);//aaa // 嵌套赋值 let obj = { p: [ 'Hello', { y: 'World' } ] }; let { p,p:[x, { y }] } = obj; console.log(x,y,p)//Hello World p: ['Hello',{ y: 'World' }] //继承赋值 const obj1 = {}; const obj2 = { foo: 'bar' }; Object.setPrototypeOf(obj1, obj2);//obj1继承obj2 const { foo } = obj1; console.log(foo) // "bar" // 默认值 // 错误的写法 let xx; // {xx} = {xx: 1};// SyntaxError: syntax error,Uncaught SyntaxError: Unexpected token '=' ({xx} = {xx: 1});//正确写法 console.log(xx) // 古怪的,等式左边可为空 // ({} = [true, false]); // 对象可解构数组 let arr = [1, 2, 3]; let {0 : first, [arr.length - 1] : last} = arr; console.log(first,last)//1 3 },
strFuc(){ // str:'yan_yan' let [a,b,c,d,e,f,g] = this.str; console.log(a,b,c,d,e,f,g)//y a n _ y a n // 对数组属性解构赋值 let {length} = this.str; console.log(length)//7 },
4、数值和布尔值的解构赋值
let {toString: s} = 123; console.log(s === Number.prototype.toString,s)//true ƒ toString() { [native code] } let {toString: ss} = true; console.log(ss === Boolean.prototype.toString,ss)// true ƒ toString() { [native code] } // 右侧必须是数组或对象,undefined和null无法转为对象,所以对它们进行解构赋值,都会报错 // let { prop: x } = undefined; // TypeError // let { prop: y } = null; // TypeError
5、函数参数的解构赋值
// 函数的解构赋值可使用默认值,注意默认值是指实参的默认值而不是形参的默认值 function move({x=1, y=1}={}) { return [x, y]; } function move1({x, y} = { x: 0, y: 0 }) { return [x, y]; } function move2({x, y=1} = { x: 0, y: 0 }) { return [x, y]; } console.log(move({x: 3, y: 8})); // [3, 8] console.log(move({x: 3})); // [3, 1] console.log(move({})); // [1, 1] console.log(move()); // [1,1] console.log(move1({x: 3, y: 8})); // [3, 8] console.log(move1({x: 3})); // [3, 1] console.log(move1({})); // [undefined, 1] console.log(move1()); // [0,0] console.log(move2({x: 3, y: 8})); // [3, 8] console.log(move2({x: 3})); // [3, 1] console.log(move2({})); // [undefined, 1] console.log(move2()); // [0,0]
6、圆括号问题 解构赋值虽然很方便,但是解析起来并不容易。对于编译器来说,一个式子到底是模式,还是表达式,没有办法从一开始就知道,必须解析到(或解析不到)等号才能知道。 由此带来的问题是,如果模式中出现圆括号怎么处理。ES6 的规则是,只要有可能导致解构的歧义,就不得使用圆括号。 可以使用圆括号的情况只有一种:赋值语句的非模式部分,可以使用圆括号 总结: 不管是哪一类的解构赋值,等式右边的数据必须是对象形式(数组也是一种对象形式) ———————————————— 版权声明:本文为CSDN博主「Yan_an_n」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/weixin_44258964/article/details/105643553
目录
HTTP协议
HTTP请求:
HTTP响应:
会话与会话状态:
Cookie
Session
Cookie和Session的区别
HTTP协议
HTTP请求:
Post /test.php HTTP/1.1 //请求行以一个方法符号开头,以空格分开,后面跟着请求的URI和协议的版本
Host: www.test.com //请求头
User-agent:mozilla/5.0(windows NT 6.1: rv: 15.0)
Gecko/20100101 firefox15.0
//空白行,代表请求头结束
Username=admin&passwd=admin //请求正文
HTTP请求方法
GET 请求获取Request-URI所标识的资源
POST 在Request-URI所标识的资源后附加新的数据
HEAD 请求获取由Request-URI所标识的资源的响应消息报头
PUT 请求服务器存储一个资源,并用Request-URI作为其标识
常用的为GET和POST;GET和POST的区别:
GET提交的内容会直接显示在URL中,私密性较差,可以用于显示一些公共资源;但是GET效率会比较高。
POST不会将内容显示在URL中,可以用于提交一些敏感数据,例如用户名或密码。
HTTP响应:
HTTP/1.1 200 OK //响应行由协议版本号,响应状态码和文本描述组成
Data:sun,15 nov 2018 11:02:04 GMT //响应头
Server:bfe/1.0.8.9
……
Connection: keep-alive
//空白行,代表响应头结束
<html>
</html><title>index.heml</title> //响应正文
HTTP的状态码:
状态代码由三位数字组成,第一个数字定义了响应的类别,且有五种可能取值。
1xx:指示信息 —— 表示请求已接收,继续处理。
2xx:成功 —— 表示请求已被成功接收、理解、接受。
3xx:重定向 —— 要完成请求必须进行更进一步的操作。
4xx:客户端错误 —— 请求有语法错误或请求无法实现。
5xx:服务器端错误 —— 服务器未能实现合法的请求。
常见状态代码、状态描述的说明如下。
200 OK:客户端请求成功。
400 Bad Request:客户端请求有语法错误,不能被服务器所理解。
401 Unauthorized:请求未经授权,这个状态代码必须和 WWW-Authenticate 报头域一起使用。
403 Forbidden:服务器收到请求,但是拒绝提供服务。
404 Not Found:请求资源不存在,举个例子:输入了错误的URL。
500 Internal Server Error:服务器发生不可预期的错误。
503 Server Unavailable:服务器当前不能处理客户端的请求,一段时间后可能恢复正常。
会话与会话状态:
Web中的会话是指一个客户端浏览器与web服务器之间连续发生一系列请求和响应过程。会话状态是指在会话过程中产生的状态信息;借助会话状态,web服务器能够把属于同一会话中的一系列的请求和响应关联起来。
Cookie
概述
Cookie是一种在客户端保持HTTP状态信息的技术,它好比商场发放的优惠卡。在浏览器访问Web服务器的某个资源时,由Web服务器在在HTTP响应头中附带传送给浏览器一片数据,web服务器传送给各个客户端浏览器的数据是可以各不相同的。
一旦Web浏览器保存了某个Cookie,那么它在以后每次访问该Web服务器是都应在HTTP请求头中将这个Cookie回传个Web服务器。Web服务器通过在HTTP响应消息中增加Set-Cookie响应头字段将CooKie信息发送给浏览器,浏览器则通过在HTTP请求消息中增加Cookie请求头字段将Cookie回传给Web服务器。
一个Cookie只能标识一种信息,它至少含有一个标识该消息的名称(NAME)和和设置值(VALUE)。一个Web浏览器也可以存储多个Web站点提供的Cookie。浏览器一般只允许存放300个Cookie,每个站点最多存放20个Cookie,每个Cookie的大小限制为4KB。
传送示意图
特点
存储于浏览器头部/传输与HTTP头部,写时带属性,读时无属性。由三元【name,domain,path】唯一确定Cookie。
Set-Cookie2响应头字段
Set-Cookie2头字段用于指定WEB服务器向客户端传送的Cookie内容,但是按照Netscape规范实现Cookie功能的WEB服务器, 使用的是Set-Cookie头字段,两者的语法和作用类似。Set-Cookie2头字段中设置的cookie内容是具有一定格式的字符串,它必须以Cookie的名称和设置值开头,格式为"名称=值”,后面可以加上0个或多个以分号(;) 和空格分隔的其它可选属性,属性格式一般为 "属性名=值”。
除了“名称=值”对必须位于最前面外,其他的可选属性可以任意。Cookie的名称只能由普通的英文ASCII字符组成,浏览器不用关心和理解Cookie的值部分的意义和格式,只要WEB服务器能理解值部分的意义就行。大多数现有的WEB服务器都是采用某种编码方式将值部分的内容编码成可打印的ASCII字符,RFC 2965规范中没有明确限定编码方式。
举例: Set-Cookie2: user-hello; Version=1; Path=/
Cookie请求头字段
Cookie请求头字段中的每个Cookie之间用逗号(,)或分号(;)分隔。在Cookie请求字段中除了必须有“名称=值”的设置外,还可以有Version、path、domain、port等属性;在Version、path、domain、port等属性名之前,都要增加一个“$”字符作为前缀。Version属性只能出现一次,且要位于Cookie请求头字段设置值的最前面,如果需要设置某个Cookie信息的Path、Domain、Port等属性,它们必须位于该Cookie信息的“名称=值”设置之后。
浏览器使用Cookie请求头字段将Cookie信息会送给Web服务器;多个Cookie信息通过一个Cookie请求头字段会送给Web服务器。
浏览器会根据下面几个规则决定是否发送某个Cookie信息:
1、请求主机名是否与某个存储的Cookie的Domain属性匹配
2、请求的端口号是否在该Cookie的Port属性列表中
3、请求的资源路径是否在该Cookie的Path属性指定的目录及子目录中
4、该Cookie的有效期是否已过
Path属性的指向子目录的Cookie排在Path属性指向父目录的Cookie之前
举例: Cookie: $Version=1; Course=Java; $Path=/hello/lesson;Course=vc; $Path=/hello
Cookie的安全属性
secure属性
当设置为true时,表示创建的Cookie会被以安全的形式向服务器传输,也就是只能在HTTPS连接中被浏览器传递到服务器端进行会话验证,如果是HTTP连接则不会传递该信息,所以不会被窃取到Cookie的具体内容。
HttpOnly属性
如果在Cookie中设置了"HttpOnly"属性,那么通过程序(JS脚本、Applet等)将无法读取到Cookie信息,这样能有效的防止XSS攻击。
总结:secure属性 是防止信息在传递的过程中被监听捕获后信息泄漏,HttpOnly属性的目的是防止程序获取cookie后进行攻击这两个属性并不能解决cookie在本机出现的信息泄漏的问题(FireFox的插件FireBug能直接看到cookie的相关信息)。
Session
使用Cookie和附加URL参数都可以将上一-次请求的状态信息传递到下一次请求中,但是如果传递的状态信息较多,将极大降低网络传输效率和增大服务器端程序处理的难度。
概述
Session技术是一种将会话状态保存在服务器端的技术,它可以比喻成是医院发放给病人的病历卡和医院为每个病人保留的病历档案的结合方式。客户端需要接收、记忆和回送Session的会话标识号,Session可以且通常是借助Cookie来传递会话标识号。
Session的跟踪机制
HttpSession对象是保持会话状态信息的存储结构,一个客户端在WEB服务器端对应一个各自的HttpSession对象。WEB服务器并不会在客户端开始访问它时就创建HttpSession对象,只有客户端访问某个能与客户端开启会话的服务端程序时,WEB应用程序才会创建一个与该客户端对应的HttpSession对象。WEB服务器为HttpSession对象分配一个独一无的会话标识号, 然后在响应消息中将这个会话标识号传递给客户端。客户端需要记住会话标识号,并在后续的每次访问请求中都把这个会话标识号传送给WEB服务器,WEB服务器端程序依据回传的会话标识号就知道这次请求是哪个客户端发出的,从而选择与之对应的HttpSession对象。
WEB应用程序创建了与某个客户端对应的HttpSession对象后,只要没有超出一个限定的空闲时间段,HttpSession对象就驻留在WEB服务器内存之中,该客户端此后访问任意的Servlet程序时,它们都使用与客户端对应的那个已存在的HttpSession对象。
Session是实现网上商城的购物车的最佳方案,存储在某个客户Session中的一个集合对象就可充当该客户的一个购物车。
超时管理
WEB服务器无法判断当前的客户端浏览器是否还会继续访问,也无法检测客户端浏览器是否关闭,所以,即使客户已经离开或关闭了浏览器,WEB服务器还要保留与之对应的HttpSession对象。随着时间的推移而不断增加新的访问客户端,WEB服务器内存中将会因此积累起大量的不再被使用的HttpSession对象,并将最终导致服务器内存耗尽。WEB服务器采用“超时限制”的办法来判断客户端是否还在继续访问如果某个客户端在一定的时间之 内没有发出后续请求,WEB服务器则认为客户端已经停止了活动,结束与该客户端的会话并将与之对应的HttpSession对象变成垃圾。
如果客户端浏览器超时后再次发出访问请求,Web服务器则认为这是一个新的会话开始,将为之创建新的Httpsession对象和分配新的会话标识号。
利用Cookie实现Session的跟踪
如果WEB服务器处理某个访问请求时创建了新的HttpSession对象,它将把会话标识号作为一个Cookie项加入到响应消息中,通常情况下,浏览器在随后发出的访问请求中又将会话标识号以Cookie的形式回传给WEB服务器。WEB服务器端程序依据回传的会话标识号就知道以前已经为该客户端创建了HttpSession对象,不必再为该客户端创建新的HttpSession对象,而是直接使用与该会话标识号匹配的HttpSession对象,通过这种方式就实现了对同一个客户端的会话状态的跟踪。
利用URL重写实现Session跟踪
Servlet规范中引入了一种补充的会话管理机制,它允许不支持Cookie的浏览器也可以与WEB服务器保持连续的会话。这种补充机制要求在响应消息的实体内容中必须包含下一 次请求的超链接,并将会话标识号作为超链接的URL地址的一个特殊参数。将会话标识号以参数形式附加在超链接的URL地址后面的技术称为URL重写。 如果在浏览器不支持Cookie或者关闭了Cookie功能的情况下,WEB服务器还要能够与浏览器实现有状态的会话,就必须对所有能被客户端访问的请求路径(包括超链接、form表单的action属性设置和重定向的URL)进行URL重写。
Cookie和Session的区别
session和cookies同样都是针对单独用户的变量(或者说是对象好像更合适点),不同的用户在访问网站的时候都会拥有各自的session或者cookies,不同用户之间互不干扰。
他们的不同点是:
1,存储位置不同
session在服务器端存储,比较安全,但是如果session较多则会影响性能
cookies在客户端存储,存在较大的安全隐患
2,生命周期不同
session生命周期在指定的时间(如20分钟) 到了之后会结束,不到指定的时间,也会随着浏览器进程的结束而结束。
cookies默认情况下也随着浏览器进程结束而结束,但如果手动指定时间,则不受浏览器进程结束的影响。
总结:简而言之,两者都是保存了用户操作的历史信息,但是存在的地方不同;而且session和cookie的目的相同,都是为了克服HTTP协议无状态的缺陷,但是完成方法不同。Session通过cookie在客户端保存session id,将用户的其他会话消息保存在服务端的session对象中;而cookie需要将所有信息都保存在客户端,因此存在着一定的安全隐患,例如本地Cookie中可能保存着用户名和密码,容易泄露。
————————————————
版权声明:本文为CSDN博主「悲观的乐观主义者」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_43997530/article/details/105650267
性能优化(网络方向)
web应用无非是两台主机之间互相传输数据包的一个过程; 如何减少传输过程的耗时就是网络方向优化的重点, 优化出发点从第一篇文章中说起
DNS解析过程的优化
当浏览器从第三方服务跨域请求资源的时候,在浏览器发起请求之前,这个第三方的跨域域名需要被解析为一个IP地址,这个过程就是DNS解析;
DNS缓存可以用来减少这个过程的耗时,DNS解析可能会增加请求的延迟,对于那些需要请求许多第三方的资源的网站而言,DNS解析的耗时延迟可能会大大降低网页加载性能。
dns-prefetch
当站点引用跨域域上的资源时,都应在<head>元素中放置dns-prefetch提示,但是要记住一些注意事项。首先,dns-prefetch仅对跨域域上的DNS查找有效,因此请避免将其用于您当前访问的站点
<link rel="dns-prefetch" href="https://fonts.googleapis.com/">
preconnect
由于dns-prefetch仅执行DNS查找,但preconnect会建立与服务器的连接。如果站点是通过HTTPS服务的,则此过程包括DNS解析,建立TCP连接以及执行TLS握手。将两者结合起来可提供机会,进一步减少跨源请求的感知延迟
<!-- 注意顺序, precontent和dns-prefetch的兼容性 -->
<link rel="preconnect" href="https://fonts.googleapis.com/" crossorigin>
<link rel="dns-prefetch" href="https://fonts.googleapis.com/">
TCP传输阶段优化
这个前端方面好像能做的有限, 我们都知道 http协议 是基于 tcp的;
升级http协议版本可以考虑下, 比如把 http/1.0 -> http/1.1 -> http/2;
这个需要我们在应用服务器上配置(nginx, Apache等), 不做概述了, 另外还需要客户端和服务器都支持哦, 目前还没开发出稳定版本,好多只支持https,不过也不远了...
http2 的优势
# 1.多路复用: 同一个tcp连接传输多个资源
这样可以突破统一域名下只允许有限个tcp同时连接,
这样http1.1所做的减少请求数优化就没有太大必要了
如多张小图合成一张大图(雪碧图),合并js和css文件
# 2.报文头压缩和二进制编码: 减少传输体积
http1 中第一次请求有完整的http报文头部,第二次请求的也是;
http2 中第一次请求有完整的http报文头部,第二次请求只会携带 path 字段;
这样就大大减少了发送的量。这个的实现要求客户端和服务同时维护一个报文头表。
# 3.Server Push
http2可以让服务先把其它很可能客户端会请求的资源(比如图片)先push发给你,
不用等到请求的时候再发送,这样可以提高页面整体的加载速度
但目前支持性不太好...emm...
总的来说, 在 c 端业务下不会太普及, 毕竟需要软件支持才行...
http 请求响应阶段优化
为了让数据包传输的更快, 我们可以从两个方面入手: 请求的数据包大小(服务器), 请求数据包的频率(客户端)
减少请求文件的大小
请求文件对应的是我们项目完成后,打包所指的静态资源文件(会被部署到服务器), 文件越小, 传输的数据包也会相对较小, 讲道理也会更快到达客户端
how to reduce a package size?
目前我们都会使用打包工具了(比如webpack, rollup, glup 等), 如何使用工具来减小包的体积呢? 这边建议您去官网文档呢...当然这里列举一下常用的手段(webpack 的), 但是注意要插件版本更新哦
JS文件压缩
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
module.exports = {
plugins: [
new UglifyJsPlugin({
// 允许并发
parallel: true,
// 开启缓存
cache: true,
compress: {
// 删除所有的console语句
drop_console: true,
// 把使用多次的静态值自动定义为变量
reduce_vars: true,
},
output: {
// 不保留注释
comment: false,
// 使输出的代码尽可能紧凑
beautify: false
}
})
]
}
CSS 文件压缩
// optimize-css-assets-webpack-plugin
plugins: [
new OptimizeCSSAssetsPlugin({
assetNameRegExp: /\.css$/g,
cssProcessor: require('cssnano'),
}),
];
html 文件压缩
// html-webpack-plugin
plugins: [
new HtmlWebpackPlugin({
template: path.join(__dirname, 'src/index.html'),
filename: 'index.html',
chunks: ['index'],
inject: true,
minify: {
html5: true,
collapseWhitespace: true,
preserveLineBreaks: false,
minifyCSS: true,
minifyJS: true,
removeComments: false,
},
}),
];
source map 文件关闭
tree shaking
1.代码不会被执行,不可到达,比如 if(false){// 这里边的代码}
2.代码执行的结果不会被用到
3.代码只会影响死变量(只写不读)
4.方法不能有副作用
// 原理相关: 以后在研究
利用 ES6 模块的特点:
只能作为模块顶层的语句出现
import 的模块名只能是字符串常量
import binding 是 immutable 的
代码擦除: uglify 阶段删除无用代码
scope hoisting(作用域提升)
分析出模块之间的依赖关系,尽可能的把打散的模块合并到一个函数中去,但前提是不能造成代码冗余
const ModuleConcatenationPlugin = require('webpack/lib/optimize/ModuleConcatenationPlugin');
module.exports = {
resolve: {
// 针对 Npm 中的第三方模块优先采用 jsnext:main 中指向的 ES6 模块化语法的文件
mainFields: ['jsnext:main', 'browser', 'main']
},
plugins: [
// 开启 Scope Hoisting
new ModuleConcatenationPlugin(),
],
};
项目中使用按需加载,懒加载(路由,组件级)
const router = new VueRouter({
routes: [
{ path: '/foo', component: () => import(/* webpackChunkName: "foo" */ './Foo.vue') }
{ path: '/bar', component: () => import(/* webpackChunkName: "bar" */ './Bar.vue') }
]
})
开启 gizp 压缩
有时候启用也会消耗服务器性能, 看情况使用吧
暂时先提这么些吧...后续想到了再加
减少请求频率
因为同一域名下 tcp 连接数的限制导致过多的请求会排队阻塞, 所以我们需要尽量控制请求的数量和频率
常见措施
将静态资源的内联到HTML中
这样这些资源无需从服务器获取, 但可能影响到渲染进程...
<!-- 1.小图片内联 base64 (url-loader) -->
<!-- 2.css内联 -->
<!-- 3.js内联 -->
<script>
${require('raw-loader!babel-loader!./node_modules/lib-flexible/flexible.js')}
</script>
利用各级缓存(下一篇存储方面介绍)
通常都是在服务端做相关配置, 但你要知道
我们可以利用http缓存(浏览器端)来减少和拦截二次请求, 当然一般都是在服务端设置的;
服务器端也可以设置缓存(redis等), 减少数据查询的时间同样可以缩短整个请求时间
利用本地存储
我们可以将常用不变的信息存在本地(cookie,storage API 等);
判断存在就不去请求相关的接口, 或者定期去请求也是可以的
花钱买 CDN 加速
CDN 又叫内容分发网络,通过把资源部署到世界各地,用户在访问时按照就近原则从离用户最近的服务器获取资源,从而加速资源的获取速度。 CDN 其实是通过优化物理链路层传输过程中的网速有限、丢包等问题来提升网速的...
购买 cdn 服务器;
然后把网页的静态资源上传到 CDN 服务上去,
在请求这些静态资源的时候需要通过 CDN 服务提供的 URL 地址去访问;
# 注意, cdn 缓存导致的新版本发布后不生效的问题
所以打包的时候常在文件后面加上 hash 值
然后在 HTML 文件中的资源引入地址也需要换成 CDN 服务提供的地址
/alicdn/xx12dsa311.js
# 利用不同域名的 cdn 去存放资源, (tcp连接限制)
webpack 构建时添加 cdn
// 静态资源的导入 URL 需要变成指向 CDN 服务的绝对路径的 URL 而不是相对于 HTML 文件的 URL。
// 静态资源的文件名称需要带上有文件内容算出来的 Hash 值,以防止被缓存。
// 不同类型的资源放到不同域名的 CDN 服务上去,以防止资源的并行加载被阻塞。
module.exports = {
// 省略 entry 配置...
output: {
// 给输出的 JavaScript 文件名称加上 Hash 值
filename: '[name]_[chunkhash:8].js',
path: path.resolve(__dirname, './dist'),
// 指定存放 JavaScript 文件的 CDN 目录 URL
publicPath: '//js.cdn.com/id/',
},
module: {
rules: [
{
// 增加对 CSS 文件的支持
test: /\.css$/,
// 提取出 Chunk 中的 CSS 代码到单独的文件中
use: ExtractTextPlugin.extract({
// 压缩 CSS 代码
use: ['css-loader?minimize'],
// 指定存放 CSS 中导入的资源(例如图片)的 CDN 目录 URL
publicPath: '//img.cdn.com/id/'
}),
},
{
// 增加对 PNG 文件的支持
test: /\.png$/,
// 给输出的 PNG 文件名称加上 Hash 值
use: ['file-loader?name=[name]_[hash:8].[ext]'],
},
// 省略其它 Loader 配置...
]
},
plugins: [
// 使用 WebPlugin 自动生成 HTML
new WebPlugin({
// HTML 模版文件所在的文件路径
template: './template.html',
// 输出的 HTML 的文件名称
filename: 'index.html',
// 指定存放 CSS 文件的 CDN 目录 URL
stylePublicPath: '//css.cdn.com/id/',
}),
new ExtractTextPlugin({
// 给输出的 CSS 文件名称加上 Hash 值
filename: `[name]_[contenthash:8].css`,
}),
// 省略代码压缩插件配置...
],
};
/*
以上代码中最核心的部分是通过 publicPath 参数设置存放静态资源的 CDN 目录 URL,
为了让不同类型的资源输出到不同的 CDN,需要分别在:
output.publicPath 中设置 JavaScript 的地址。
css-loader.publicPath 中设置被 CSS 导入的资源的的地址。
WebPlugin.stylePublicPath 中设置 CSS 文件的地址。
设置好 publicPath 后,WebPlugin 在生成 HTML 文件和 css-loader 转换 CSS 代码时,会考虑到配置中的 publicPath,用对应的线上地址替换原来的相对地址。
*/
参考
DNS MDN]
webpack 文档
深入浅出 Webpack
Scope Hoisting
在了解了javascript的语言基础和特性后
javascript真正大放光彩的地方来了——这就是javascript DOM
Javascript DOM
DOM(Document Object Model),文档对象模型。
是W3C组织推荐的处理可扩展标记语言(HTML或者XML)的标准编程接口;W3C已经定义了一系列DOM接口,通过这些DOM接口可以改变网页的内容、结构和样式。
简单的说就是一套操作文档内容的方法。
需要注意的是,我们需要把DOM当作一个整体,不能分割看待,即DOM(文档对象模型)是一套操作文档内容的方法。
DOM把以上内容看作都是对象
<!DOCTYPE html> <html> <head> <title>Shopping list</title> <meta charset="utf-8"> </head> <body> <h1>What to buy</h1> <p id="buy" title="a gentle reminder">Don't forget to buy this stuff</p> <ul id="purchases"> <li>A tin od beans</li> <li>Cheese</li> <li>Milk</li> </ul> </body> </html>
1、获取DOM四种基本方法
1、getElementById()
2、getElementsByTagname()
3、getAttribute()
4、setAttribute()
常用的两种解析:
1. getElementById():
参数:元素的ID值。 (元素节点简称元素)
返回值:一个有指定ID的元素对象(元素是对象)
注:这个方法是与document对象相关联,只能由document对象调用。
用法:document.getElementById(Id)
例:
<!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <div id="time">2020-04-16</div> <script> // 1. 因为我们文档页面从上往下加载,所以先得有标签 所以我们script写到标签的下面 // 2. get 获得 element 元素 by 通过 驼峰命名法 // 3. 参数 id是大小写敏感的字符串 // 4. 返回的是一个元素对象 var timer = document.getElementById('time'); console.log(timer); console.log(typeof timer); // 5. console.dir 打印我们返回的元素对象 更好的查看里面的属性和方法 console.dir(timer); </script> </body> </html>
看一下控制台打印的是什么
可以看到 console.log(timer)打印出来的是整个div标签
timer类型是个对象
2. getElementsByTagName():
参数:元素名
返回值:一个对象数组。这个数组里每个元素都是对象,每个对象分别对应着文档里给定标签的一个元素。
注:这个方法可和一般元素关联。这个方法允许我们把通配符当作它的参数,返回在某份html文档里总共有多少个元素节点。
用法:element.getElementsByTagName(TagName)
例:
var items=document.getElementsByTagName("li");
items.length;//3
document.getElementsByTagName(“*”);//12
2、事件基础
3.1 事件概述
JavaScript使我们有能力创建动态页面,而事件是可以被JavaScript侦测到的行为。
简单理解:触发——>响应机制
网页中每个元素都可以产生某些可以触发JavaScript的事件,例如,我们可以在用户点击某按钮产生一个事件,然后去执行某些操作
3.2 事件三要素
事件源 、事件类型、事件处理程序,我们也称为事件三要素
(1) 事件源 事件被触发的对象 谁
(2) 事件类型 如何触发 什么事件 比如鼠标点击(onclick) 还是鼠标经过 还是键盘按下
(3) 事件处理程序 通过一个函数赋值的方式 完成
代码实例
<!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <button id="btn">唐伯虎</button> <script> // 点击一个按钮,弹出对话框 // 1. 事件是有三部分组成 事件源 事件类型 事件处理程序 我们也称为事件三要素 //(1) 事件源 事件被触发的对象 谁 按钮 var btn = document.getElementById('btn'); //(2) 事件类型 如何触发 什么事件 比如鼠标点击(onclick) 还是鼠标经过 还是键盘按下 //(3) 事件处理程序 通过一个函数赋值的方式 完成 btn.onclick = function() { alert('点秋香'); } </script> </body> </html>
运行结果
1、获取事件源
2、注册事件(绑定事件)
3、添加事件处理程序(采取函数赋值形式)
代码实战
-
<!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <div>123</div> <script> // 执行事件步骤 // 点击div 控制台输出 我被选中了 // 1. 获取事件源 var div = document.querySelector('div'); // 2.绑定事件 注册事件 // div.onclick // 3.添加事件处理程序 div.onclick = function() { console.log('我被选中了'); } </script> </body> </html>
常用的DOM事件
onclick事件---当用户点击时执行
onload事件---当用户进入时执行
onunload事件---用用户离开时执行
onmouseover事件---当用户鼠标指针移入时执行
onmouseout事件---当用户鼠标指针移出时执行
onmousedown事件---当用户鼠标摁下时执行
onmouseup事件---当用户鼠标松开时执行
————————————————
版权声明:本文为CSDN博主「那是我呐」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_42402867/article/details/105567787
文章目录
继承性的描述:
继承性是指被包在内部的标签将拥有外部标签的样式性,即子元素可以继承父类的属性。
例:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> div{ color: blue; } </style> </head> <body> <div>父元素 <div>子元素 <p>我依旧是子元素</p> </div> </div> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> p{ font-size: 32px; } </style> </head> <body> <p style="color: blue;">我这里体现了层叠性呀</p> </body> </html>
使用结论
由于内容有限,但是结论是一定的,所以我直接给出结论:
若多个选择器定义的样式不冲突,则元素应用所有选择器定义的样式。
若多个选择器定义的样式发生冲突(比如:同时定义了字体颜色属性),则CSS按照选择器的优先级,让元素应用优先级搞得选择器样式。
CSS定义的选择器优先级从高到低为:行内样式–>ID样式–>类样式–>标记样式。
如若想直接定义使用哪个样式,不考虑优先级的话,则使用!important,把这个加在样式后面就行了。
优先级
定义CSS样式时,经常出现两个或更多规则应用在同一个元素上,这时就会出现优先级的问题。层叠性和选择器的圈中有很大的关系。
优先级的使用说明
权重分析:
内联样式:如:style="",权重为1000。
ID选择器,如:#content,权重为100。
类,伪类和属性选择器,如.content,权重为10。
标签选择器和伪元素选择器,如div p,权重为1。
继承样式,权重为0。
将基本选择器的权重相加之和,就是权重大小,值越大,权重越高。
计算权重方法
数标签:先数权重最高的标签,然后数第二高权重的标签,以此类推,就会生成一个数组,里面包含四个数字。
比如(0,0,0,0)分别对应(行内式个数,id选择器个数,类选择器个数,标签选择器个数)
然后两个选择器通过对别四个数字的大小,确定权重关系。
例:
#box ul li a.cur有1个id标签,1个类,3个标签,那么4个0就是(0,1,1,3)
.nav ul .active .cur有0个id,3个类,1个标签,那么4个0就是(0,0,3,1)
例:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> .p1{ color: blue; } #p1{ color: red; } </style> </head> <body> <p id="p1" class="p1">我们来试一下优先级</p> </body> </html>
先推测一波,因为前面讲到了ID选择器的权重是大于类选择器的,所以这里颜色应该为red。
效果如下:
推测正确!优先级GET!
今天讲一下使用vant Swipe 轮播控件过程中遇到的问题
主要是使用swiper自定义的大小的时候,宽度适应不同分辨率的移动设备
适应宽度的同时还需控件的正常使用
先看一下需要实现的功能,
一个简单的轮播图,但是每个轮播的宽度需要低于100%,使第二个轮播的van-swipe-item可以展示到第一个位置一部分
这时我们再去vant的文档查看一下控件
刚好有一个自定义控件大小的可以使用,完美解决了我们的问题
当我们使用控件之后
<van-swipe :loop="false" @change="onChange" :width="350"> <van-swipe-item v-bind:id="item0"><div class="swipe0"> <div class="contion"> <p class="title">家中有事,申请请假一天</p> <p class="title1"><span class="rice"></span>部门经理核审中</p> <p class="time">03.8 14.25</p> <p class="type">放假申请</p> </div> <img src="../../assets/images/index/xx/fangjia.png"> </div></van-swipe-item> <van-swipe-item ><div class="swipe1"></div></van-swipe-item> <van-swipe-item ><div class="swipe2"></div></van-swipe-item> <template #indicator> <div class="custom-indicator"> {{ current + 1 }}/3 </div> </template> </van-swipe>
发现功能可以使用,但是再 iPhone8/7 plus 以及iPhone5/se 等分辨率下出现了宽度固定而不适应的情况,
简单来说,我们把van-swipe-item宽度控制在了80% 第二个van-swipe-item自然可以展示出来一部分
但是当滑到第二页的时候 由于第一页的宽度还是80% 所以就出现了这样的情况,所以我打算采用
动态的改变 滑动到第几页的时候 把当页的宽度变为80% 其他页保持不变,
于是
<van-swipe :loop="false" @change="onChange" > <van-swipe-item v-bind:id="item0"><div class="swipe0"> <div class="contion"> <p class="title">家中有事,申请请假一天</p> <p class="title1"><span class="rice"></span>部门经理核审中</p> <p class="time">03.8 14.25</p> <p class="type">放假申请</p> </div> <img src="../../assets/images/index/xx/fangjia.png"> </div></van-swipe-item> <van-swipe-item v-bind:id="item1"><div class="swipe1"></div></van-swipe-item> <van-swipe-item v-bind:id="item2"><div class="swipe2"></div></van-swipe-item> <template #indicator> <div class="custom-indicator"> {{ current + 1 }}/3 </div> </template> </van-swipe>
首先 我们为每个swipe-item添加id
data(){ return { android: true, ios: true, iphoneX: true, current: 0, item0:'item0', item1:'item1', item2:'item2', } }, mounted(){ }, methods: { onChange(index){ console.log('当前 Swipe 索引:' + index); if(index==1){ var div =document.getElementById("item0").style.setProperty('width', '10rem', 'important'); var div1 =document.getElementById("item1").style.setProperty('width', '9.3333333rem', 'important'); var div2 =document.getElementById("item2").style.setProperty('width', '9.3333333rem', 'important'); } else if(index==2){ var div1 =document.getElementById("item1").style.setProperty('width', '10rem', 'important'); var div0 =document.getElementById("item0").style.setProperty('width', '10rem', 'important'); var div2 =document.getElementById("item2").style.setProperty('width', '9.3333333rem', 'important'); } else if(index==0){ var div =document.getElementById("item2"); var div0 =document.getElementById("item0").style.setProperty('width', '9.3333333rem', 'important'); var div1 =document.getElementById("item1").style.setProperty('width', '9.3333333rem', 'important'); } },
此外,监听滑动事件,根据滑动到第几页 更改当前页面的宽度,
这样就解决了
兰兰设计:前端达人
emcc [options] file ...
这个输入文件file,既可以是clang可以编译的C/C++语言,也可以是二进制形式的llvm bitcode或者人类可读形式的llvm assembly文件。
大部分clang或者gcc的选项(option)都是可以工作的,比如:
# 显示信息 emcc --help # 显示编译器版本信息 emcc --version
如果想看当前Emscripten中clang版本支持的全部选项列表,可以直接使用命令:
clang --help.
emcc修改的或者emcc中新的选项列在下面:
首先是一些编译优化flag,它们-O0,-O1,-O2,-Os,-Oz,-O3。
-O0:
不进行编译优化(这是默认情况)。当你刚开始移植项目是推荐使用它,因为它会包含许多断言。
-O1:
简单优化。推荐你在既想缩短编译时间又想编译优化时使用。它毕竟比-O2级别的优化编译起来快多了。它会进行asm.js和llvm的-O1进行优化,它会relooping,会删除运行时断言和C++异常捕获,它也会使得-s ALIASING_FUNCTION_POINTERS=1。
想要C++异常捕获重新可用,请设置:-s DISABLE_EXCEPTION_CATCHING=0。
-O2:
和-O1类似,不过多了JavaScript级别的优化以及一些llvm -O3的优化项。当你想发布项目的时候,推荐使用本级别优化。
-O3:
和-O2类似,不过比-O2又多了一些JavaScript优化,而且编译时间明显比-O2长。这个也推荐在发布版本的时候使用。
-Os:
和-O3类似,不过增加了额外的优化以减小生成的代码体积,代价是比-O3性能差一点。-Os优化会同时影响llvm bitcode 和JavaScript文件的生成。
-Oz:
和-Os类似,不过进一步减小了代码体积。
-s OPTION=VALUE
传给编译器的所有涉及到JavaScript代码生成的选项。选项列表,请见settings.js。
对于某个选项的值,不仅可以直接在emcc命令行里面设定,也可以把他们写成json文件。比如下面,就是将DEAD_FUNCTIONS选项的值放到了path/to/file文件里,emcc里面传这个文件的路径。
-s DEAD_FUNCTIONS=@/path/to/file
note: 1、文件内容可以是:["_func1","_func2"]; 2、文件路径必须是绝对的,不能是相对的。
-g:
这是保留调试信息flag。
-g<level>
控制打印的调试信息数量,每一个level都是在前一个level的基础上编译的:
note:
优化级别越高,编译时间越长
--profiling:
--profiling-funcs:
--tracing:
启用Emscripten的tracing API。
--emit-symbol-map:
--js-opts<level>:
允许JavaScript优化,有两个值:
0:不允许JavaScript优化器允许;
1:使用JavaScript优化器。
通常用不到我们设置这一项, 因为设置-O后面的level的时候,这个项就能顺便取到一个合适的值。
note:
有些选项会重写这个flag的值,比如EMTERPRETIFY, DEAD_FUNCTIONS, OUTLINING_LIMIT, SAFE_HEAP 和 SPLIT_MEMORY会将js-opts=1,因为他们依赖js优化器。
--llvm-opts<level>:
启用llvm优化。它的取值有有:
和--js-opts<level>一样,通常用不到我们设置这一项, 因为设置-O后面的level的时候,这个项就能顺便取到一个合适的值。
--llvm-lto<level>:
启用llvm 连接时 优化。可以取值0,1,2,3。
--closure <on>:
运行压缩编译器(Closure Compiler),可能的取值有,0,1,2:
--pre-js <file>
生成代码前,指定一个要把内容添加进来的文件。
--post-js <file>
生成代码后,指定一个要把内容添加进来的文件。
--embed-file <file>
指定一个带路径的文件嵌入到编译生成的js代码里。路径是相对于编译时的当前路径。如果传的是一个目录,则目录下所有文件的内容都会被嵌入到将来生成的js代码中。
--preload-file <name>
异步运行编译代码前,指定一个预加载的文件。路径是相对于编译时的当前路径。如果传的是一个目录,则目录下所有文件的内容都会被预加载到一个.data文件中。
--exclude-file <name>
从 –embed-file and –preload-file后面的目录中排除一些文件,支持使用通配符*。
--use-preload-plugins
告诉文件打包器当文件加载时,运行预加载插件。它用来执行诸如使用浏览器解码器解码图片和音频等。
--shell-file <path>
指定要生成HTML的模板文件。
--source-map-base <base-url>
--minify 0
等于-g1。
--js-transform <cmd>
优化之前,生成代码之后,设定这一条命令。这条命令可以让你修改JavaScript代码。之后,编译器会将修改的和未修改的一起进行编译优化。
--bind
启用bingdings编译源代码。bingings是Emscripten中连接C++和JavaScript代码的一类API。
--ignore-dynamic-linking
告诉编译器忽视动态链接,之后用户就得手动链接到共享库。
--js-library <lib>
定义除了核心库(src/library_*)以外的js库。
-v
打开详细输出。
这个设置为把-v传给clang,并且启用EMCC_DEBUG生成编译阶段的中间文件。它也会运行Emscripten关于工具链的内部的完整性检查。
tip: emcc -v是诊断错误的有用工具,不管你是否附加其他参数。
--cache
--clear-cache
--clear-ports
--show-ports
--save-bc PATH
--memory-init-file <on>
规定是否单独生成一个内存初始化文件。取值包括0和1.
-Wwarn-absolute-paths
启用在-I和-L命令行指令中使用绝对路径的警告。这是用来警告无意中使用了绝对路径的。在引用非可移植的本地系统头文件时,使用绝对路径有时是很危险的。
--proxy-to-worker
--emrun
使生成的代码能够感知emrun命令行工具。当运行emran生成的应用程序时,这样设置就允许stdout、stderr和exit(returncode)被捕获。
--cpuprofiler
在生成的页面上嵌入一个简单的CPU分析器。使用这个来执行粗略的交互式性能分析。
--memoryprofiler
在生成的页面上嵌入内存分配跟踪器,使用它来分析应用程序Emscripten堆的使用情况。
--threadprofiler
在生成的页面上嵌入一个线程活动分析器。当进行多线程编译时,使用它来分析多线程应用程序。
--em-config
--default-obj-ext .ext
--valid-abspath path
设置一个绝对路径的白名单,以防止关于绝对路径的警告。
-o <target>
编译输出的文件格式。target可以取值为:
note:
如果你用了--memory-init-file,则还会从js文件中再单独分出一部分代码为.mem文件。
-c
生成llvm bitcode代码,而不是JavaScript。
--separate-asm
把asm.js文件单独生成到一个文件中。这样可以减少启动时的内存加载。
--output_eol windows|linux
规定生成的文本文件的行尾,如果是–output_eol windows,就是windows rn行尾,如果是–output_eol linux,则生成Linux行尾的文本文件。
--cflags
emcc会受到几个环境变量的影响,如下:
这几个里面比较有意思的是EMCC_DEBUG。比如,如果你在编译之前设置set EMCC_DEBUG=1,那么编译的时候会把编译过程的调试信息和编译各个阶段的中间文件输出到一个临时目录,这算是给开发者提供一些编译期间的帮助或者说调试信息吧。
Emscripten主题系列文章是emscripten中文站点的一部分内容。
第一个主题介绍代码可移植性与限制
第二个主题介绍Emscripten的运行时环境
第三个主题第一篇文章介绍连接C++和JavaScript
第三个主题第二篇文章介绍embind
第四个主题介绍文件和文件系统
第六个主题介绍Emscripten如何调试代码
这篇文章的主要目的是学会使用koa框架搭建web服务,从而提供一些后端接口,供前端调用。
搭建这个环境的目的是: 前端工程师在跟后台工程师商定了接口但还未联调之前,涉及到向后端请求数据的功能能够走前端工程师自己搭建的http路径,而不是直接在前端写几个死数据。即,模拟后端接口。
当然在这整个过程(搭建环境 + 开发示例demo)中,涉及到以下几点知识点。
包括:
首先是vue + vue-router + vuex的环境。我们用vue-cli脚手架生成项目,会用vue的同学对这块应该很熟了。
// 全局安装脚手架工具 npm i vue-cli -g // 验证脚手架工具安装成功与否 vue --version // 构建项目 vue init webpack 项目名 // 测试vue项目是否运行成功 npm run dev
因为脚手架生成的vue项目不包含vuex,所以再安装vuex。
// 安装vuex npm i vuex --save
前端项目构建好了,就开始构建我们的后端服务。
首先在你的开发工具(不管是webstorm还是sublime)里新建一个目录,用来搭建基于koa的web服务。
在这里,我们不妨给这个目录起名为koa-demo。
然后执行:
// 进入目录 cd koa-demo // 生成package.json npm init -y // 安装以下依赖项 npm i koa npm i koa-router npm i koa-cors
安装好koa和两个中间件,环境就算搭建完成了。
搭建环境是为了使用,所以我们立马来写一个demo出来。
demo开发既是一个练习如何在开发环境中写代码的过程,反过来,也是一个验证环境搭建的对不对、好不好用的过程。
本例中,后端我们只提供一个服务,就是给前端提供一个返回json数据的接口。代码中包含注释,所以直接上代码。
server.js文件
// server.js文件 let Koa = require('koa'); let Router = require('koa-router'); let cors = require('koa-cors'); // 引入modejs的文件系统API let fs = require('fs'); const app = new Koa(); const router = new Router(); // 提供一个/getJson接口 router
.get('/getJson', async ctx => { // 后端允许cors跨域请求 await cors(); // 返回给前端的数据 ctx.body = JSON.parse(fs.readFileSync( './static/material.json'));
}); // 将koa和两个中间件连起来 app.use(router.routes()).use(router.allowedMethods()); // 监听3000端口 app.listen(3000);
这里面用到了一个json文件,在'./static/material.json'路径,该json文件的代码是:
// material.json文件 [{ "id": 1, "date": "2016-05-02", "name": "张三", "address": "北京 清华大学",
}, { "id": 2, "date": "2016-05-04", "name": "李四", "address": "上海 复旦大学",
}, { "id": 3, "date": "2016-05-01", "name": "王五", "address": "广东 中山大学",
}, { "id": 4, "date": "2016-05-03", "name": "赵六", "address": "广东 深圳大学",
}, { "id": 5, "date": "2016-05-05", "name": "韩梅梅", "address": "四川 四川大学",
}, { "id": 6, "date": "2016-05-11", "name": "刘小律", "address": "湖南 中南大学",
}, { "id": 7, "date": "2016-04-13", "name": "曾坦", "address": "江苏 南京大学",
}]
然后我们是用以下命令将服务启动
node server.js
打开浏览器,输入http://127.0.0.1:3000/getJson。看一看页面上是否将json文件中的json数据显示出来,如果能够显示出来,则说明这个提供json数据的服务,我们已经搭建好了。
为突出重点,排除干扰,方便理解。我们的前端就写一个组件,组件有两部分:首先是一个按钮,用来调用web服务的getJson接口;然后是一个内容展示区域,拿到后端返回的数据以后,将其在组件的这块区域显示出来。
首先我们看组件文件吧
<template> <div class="test"> <button type="button" @click="getJson">从后端取json</button> <div class="showJson">{{json}}</div> </div> </template> <script> import {store} from '../vuex' export default { computed: {
json(){ return store.state.json;
}
}, methods: {
getJson(){
store.dispatch("getJson");
}
}
} </script> <style scoped> .showJson{ width:500px; margin:10px auto; min-height:500px; background-color: palegreen;
} </style>
非常简单,就不多解释了。
然后看我们的vuex文件。
import Vue from 'vue' import Vuex from 'vuex';
Vue.use(Vuex) const state = { json: [],
}; const mutations = {
setJson(state, db){
state.json = db;
}
} const actions = {
getJson(context){ // 调用我们的后端getJson接口 fetch('http://127.0.0.1:3000/json', { method: 'GET', // mode:'cors', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json',
},
}).then(function (res) { if(res.status === 200){ return res.json()
}
}).then(function (json) { //console.log(typeof Array.from(json), Array.from(json)); context.commit('setJson', Array.from(json));
})
}
}; export const store = new Vuex.Store({ state: state, mutations: mutations, actions: actions,
})
ok, 代码撸完了,获取后端数据之前是这样的。
获取后端数据之后是这样的。
想要把本demo的fetch改为axios方式,要做的工作有以下几处:
1、安装axios、在vuex文件引用axios
npm i axios import axios from 'axios'
2、将fetch部分代码替换为:
const actions = {
getJson(context){
axios.get('/json', { method: 'GET', // mode:'cors', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json',
},
}).then(function (res) { if(res.status === 200){ return res.data
}
}).then(function (json) { //console.log(typeof Array.from(json), Array.from(json)); context.commit('setJson', Array.from(json));
})
}
};
3、又会遇到跨域,在webpack中修改,路径config/index.js文件中添加proxyTable项的配置:
proxyTable: { '/json': { target: 'http://127.0.0.1:3000', changeOrigin: true, pathRewrite: { '^/json': '/json' }
}
},
基于vue脚手架搭建的项目,模拟异步取数据,也可以直接在脚手架生成的static文件夹下放置数据,假装是后台拿过来的数据。
不过搭建一个基于express或者koa的web服务,确实也该是一个前端工程师应该掌握的。
OK,以上就是全文了。
如果这篇文章使你有所收获,不胜荣幸。
欢迎点赞,以期能帮助更多同学!
https://github.com/xiangshuo1992/preload.git git@github.com:xiangshuo1992/preload.git
这两个地址展示的是同一个项目,但是这两个地址之间有什么联系呢? 前者是https url 直接有效网址打开,但是用户每次通过git提交的时候都要输入用户名和密码,有没有简单的一点的办法,一次配置,永久使用呢?当然,所以有了第二种地址,也就是SSH URL,那如何配置就是本文要分享的内容。 GitHub配置SSH Key的目的是为了帮助我们在通过git提交代码是,不需要繁琐的验证过程,简化操作流程。 步骤一、设置git的user name和email
如果你是第一次使用,或者还没有配置过的话需要操作一下命令,自行替换相应字段。git config --global user.name "Luke.Deng" git config --global user.email "xiangshuo1992@gmail.com"
二、检查是否存在SSH Key
cd ~/.ssh ls 或者 ll //看是否存在 id_rsa 和 id_rsa.pub文件,如果存在,说明已经有SSH Key
如果没有SSH Key,则需要先生成一下
ssh-keygen -t rsa -C "xiangshuo1992@gmail.com"
三、获取SSH Key
cat id_rsa.pub //拷贝秘钥 ssh-rsa开头
四、GitHub添加SSH Key
GitHub点击用户头像,选择setting
新建一个SSH Key
取个名字,把之前拷贝的秘钥复制进去,添加就好啦。
五、验证和修改
测试是否成功配置SSH Key
ssh -T git@github.com //运行结果出现类似如下 Hi xiangshuo1992! You've successfully authenticated, but GitHub does not provide shell access.之前已经是https的链接,现在想要用SSH提交怎么办?
直接修改项目目录下.git
文件夹下的config
文件,将地址修改一下就好了。
———————————————— 版权声明:本文为CSDN博主「前端向朔」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/u013778905/java/article/details/83501204
蓝蓝设计的小编 http://www.lanlanwork.com