在vue项目中,经常会遇到需要刷新当前页面的需求。
因为vue-router判断如果路由没有变化,是不会刷新页面获取数据的。
方式1:go(0)和reload()
通过location.reload()或是this.$router.go(0)两种强制刷新方式,相当于按F5,会出现瞬间白屏,体验差,不推荐。
方式2:定义一个空白路由页面,路由跳转到该空白页后立马跳回当前页,实现路由刷新。
在router路由表中定义一个空白路由,
// 强制刷新当前页所用的中间跳转页
{
path: '/redirect/:path*',
component: () => import('@/views/redirect/index')
}
写一个空白路由组件
//redirect/index
<script>
export default {
created() {
const { params, query } = this.$route
const { path } = params
this.$router.replace({ path: '/' + path, query })
},
render: function(h) {
return h() // avoid warning message
}
}
</script>
在需要刷新的页面使用
refresh() {
// 刷新当前路由
const { fullPath } = this.$route
this.$router.replace({
path: '/redirect' + fullPath
})
}
这种方式,基本上能够应付绝大多数情况,推荐使用。
但是,有时候,有一些极端情况下,这种刷新不起作用,而又不想用第一种那种毛子般的简单粗暴的方式的话,下面的方式可以选择使用。
方式3:provede/inject 方式
vue官方文档说了,这个依赖注入方式是给插件开发使用的,普通应用中不推荐使用。
但是,效果却很好。
app.vue修改
<template>
<div id="app">
<router-view v-if="isRouterAlive" />
</div>
</template>
<script>
export default {
name: 'App',
provide() {
return {
reload: this.reload
}
},
data() {
return {
isRouterAlive: true
}
},
methods: {
reload() {
this.isRouterAlive = false
this.$nextTick(function(){
this.isRouterAlive = true
})
}
}
}
</script>
使用的时候:
demo.vue
<template>
<div class="container">
xxx
</div>
</template>
<script>
export default {
inject: ['reload], // 依赖注入
name: 'Demo',
computed: {
message() {
return '抱歉,您访问的页面地址有误或者该页面不存在...'
}
},
methods: {
handleReload() {
this.reload() // 直接在需要刷新的方法中调用这个reload()
}
}
}
</script>
<style lang="scss" scoped>
</style>
原理就是通过依赖注入的方式,在顶部app通过v-if的显示隐藏来强制切换显示,以此来让vue重新渲染整个页面,app中通过provide方式定义的reload方法,在它的后代组件中,无论嵌套多深,都能够触发调用这个方法。具体说明查看官方文档。
说到渲染引擎就不得不提到延迟渲染,基本上一个引擎如果没有实现延迟渲染就不能说是一个好的渲染引擎,不过可惜的是three.js并没有实现延迟渲染(ps:呼吁作者赶紧实现mrt吧)。由于没有mrt和延迟渲染,本来不打算写后处理的,但是即使没有,我们也希望能实现一些炫酷的效果,那就在现在的基础上对three.js进行简单的改造来实现一套高性能的后处理渲染器吧。
要实现后处理我们首先要考虑需求,是否要兼顾移动端,是否要兼顾大屏(4k),是否要支持webgl1,是否要在各种显卡中都有一个还算不错的性能。目前我主要考虑的是:性能要好,可以兼顾大屏,不打算完美支持webgl1,尽量多使用webgl2的特性。后面所有的性能测试为都以N卡作为性能测试指标,先不管AMD卡(AMD抗锯齿的处理性能会高些的,但是动态处理性能会偏低,这里有很多细节问题)
好了,我们关心的是好的性能,尽量多使用webgl2的特性,尽量能兼顾大屏(这里主要是要注意显存问题),现在开始准备我们的渲染器吧
要完成一个后处理渲染器,我们首先要考虑抗锯齿,常用的有超采样技术和多重采样技术,具体可以看这个介绍 添加链接描述
three.js已经实现了SSAA,SMAA,TAA这三种超采样技术,效果都还不错,具体实现three.js都有例子,就不详细说明了。SSAA抗锯齿效果是最好的,但是性能最差,现实情况下根本无法使用。SMAA性能会好一些,2000个物体差不多掉15帧吧(SMAA比FXAA计算稍微复杂一些,SMAA研究的少,不知道能不能解决line的锯齿问题,如果知道的欢迎留言)。SMAA效果基本可以接受,但是2000个物体掉的帧率还是有点多,这个也不是我们首选的。TAA效果很好,如果场景里面很少动的东西,它是个不错的选择,如果有运动的物体或者动画等等,基本上抗锯齿就没效果了,理论上应该可以实现动态的TAA,但是用目前的技术很难实现。所以TAA先不考虑加入我们的后处理渲染器(真实使用场景下一般都有动画或者贴图流动效果,所以TAA无法使用)。
SMAA:效果不错,开销15帧左右(2000物体)
SSAA:效果最好,开销太大 (2000物体基本已经没有了帧率)
TAA:效果基本和SSAA差不多,但目前只有静止的时候才有效果
再来看看多重采样MSAA,这个特性必须使用webgl2,是webgl提供的方式,和浏览器自身的抗锯齿原理一样,效果不错,和正常渲染的结果没有区别,性能开销也不是太大,前提是我们不要使用stencilbuffer。而且three.js MSAA这块的释放有些小问题,详细可以在deallocateRenderTarget这个接口中进行修改。现在我们可以把MSAA作为主要的抗锯齿技术。MSAA虽然性能开销不大,但是唯一的不足是比较吃显存,如果是大屏,而且显卡不好的话还是容易崩,无法开启。因此我们还要继续选择一个开销小不吃显存的抗锯齿(当然也可以考虑SMAA,目前由于帧率开销较大(2000物体掉了15帧左右),我们不考虑加入SMAA)
MSAA:基本没有帧率开销,效果不错,但是耗费显存(2000物体)
最后的备选方案就是FXAA了,FXAA可以参考这个文章:添加链接描述讲的很详细,FXAA性能开销很小,但是效果很一般,特别是细线的锯齿没法解决,转动摄影机边缘的闪动效果也无法解决,而且还有一个问题是由于FXAA就是靠边缘模糊抗锯齿,所以必然导致画面会略有模糊。但是它最大的好处是开销很小而且不耗费显存,并且集成到后处理渲染器中最简单。但是当显存不足显卡太差的时候它还是个不错的选择。因此需要加入FXAA。
FXAA:效果一般,开销很小,无法解决线的锯齿问题,带来模糊(2000物体)
我们看了下各大引擎,基本每个引擎都实现了FXAA,还有很多引擎实现了FXAA3,FXAA3效果会好一些,但是依然无法解决线的问题。cesium的抗锯齿就是完全采用FXAA3,效果还可以接受,所以目前我们把cesium的fxaa3_11拿过来用,最终引擎选择使用MSAA加FXAA3_11的抗锯齿策略(当然SMAA和TAA也可以选择)。
HTML中如何引入图标
在头部引用在HTML中里面引用一个东西,。在头部引用<script defer src="https://use.fontawesome.com/releases/v5.0.8/js/all.js"></script>
然后在网上搜索font awesome。可以进那个官方(外国网站比较卡)。以下为点进那个中文网–字体图标
点击右上角图标库
3随便点一个进去
4复制<i class="fa fa-address-book-o" aria-hidden="true"></i>
引入你的代码中<span><i class="fa fa-address-book-o" aria-hidden="true"></i>></span>
<div><i class="fa fa-address-book-o" aria-hidden="true"></i>></div>
1
在div或span里引用,可以实现块状元素和行内元素的功能,不用管i标签,改变图标大小用font-size,调上下高度line-height,左右位置text-align。
部分图标可以引用不了,大致样式
3.完美是最无情的禁锢
是什么
ES5提供的数组的方法。
reduce() 方法接收一个函数作为回调函数(accumulator),数组中的每个值(从左到右)开始缩减(其实就是从左往右开始对每个数执行回调函数),最终为一个值。
PS: 回调函数的返回结果类型和传入的初始值相同
语法以及参数
arr.reduce( callback(accumulator, currentValue,index ,array ) ,initialValue )
1
initialValue 可选
如果有的话则作为,第一次调用 callback函数时的第一个参数的值。
如果没有提供初始值,callback则使用数组的第一个元素,作为第一次调用的初始值。
在没有初始值的空数组上调用 reduce 将报错。
accumulator
默认传入上一次调用回调函数的的返回值。
初始值: initialValue存在的话,则是initialValue 若没有则是数组的第一个元素
currentValue
数组中正在处理的元素。
index 可选
数组中正在处理的当前元素的索引。 如果提供了initialValue,则起始索引号为0,否则从索引1起始。
array可选
调用reduce()的数组
一个小小的例子
例1 无initialValue
var arr = [1, 2, 3, 4, 5];
sum = arr.reduce(function(result, cur, index, arr) {
console.log(result, cur, index,arr);
return result+ cur;
})
console.log(sum) // 最后的结果是15
result cur index arr
第1次 1 2 1 [1, 2, 3, 4, 5]
第2次 3 3 2 [1, 2, 3, 4, 5]
第3次 6 4 3 [1, 2, 3, 4, 5]
第4次 10 5 4 [1, 2, 3, 4, 5]
例2 有initialValue 传入10
var arr = [1, 2, 3, 4, 5];
sum = arr.reduce(function(result, cur, index, arr) {
console.log(result, cur, index,arr);
return result+ cur;
},10)
console.log(sum) // 最后的结果是25
result cur index arr
第1次 10 1 0 [1, 2, 3, 4, 5]
第2次 11 2 1 [1, 2, 3, 4, 5]
第3次 13 3 2 [1, 2, 3, 4, 5]
第4次 16 4 3 [1, 2, 3, 4, 5]
第5次 20 5 4 [1, 2, 3, 4, 5]
回调函数的返回值
上面的例子返回的都是一个整型数字,如果希望返回其他类型的数据呢?
这个就跟accumulator的初始值有关系了。
下面的例子我们传入的是一个object {sum: 0}
var items = [0,1,2,3,4];
var reducer = function add(sumT, item) {
console.log(sumT)
sumT.sum = sumT.sum + item;
return sumT;
};
var total = items.reduce(reducer, {sum: 0});
console.log(total); // {sum:1130}
运行结果
{sum: 0}
{sum: 1}
{sum: 3}
{sum: 6}
{sum: 10}
reduce()的应用
node系列之数据接口注册登陆接口的实现
1、使用express脚手架创建项目
2、了解项目的目录结构
3、准备数据库相关文件
4、编写注册接口
5、编写登陆接口
6、验证登陆实现
7、预告
1、使用express脚手架创建项目
// 安装脚手架,只需安装一次
npm i express-generator -g
// 创建express项目
express myapp --view=ejs
cd myapp
// 安装依赖
npm i
// 安装需要使用的模块
// 数据库模块 用户唯一id模块 密码加密模块 token模块
npm i mongoose node-uuid bcryptjs jsonwebtoken -S
2、了解项目的目录结构
bin
www ------- 服务器启动
node_modules ------- 项目的依赖文件
public ------- 静态资源文件夹
images ------- 静态图片
javascripts ------- 静态的js文件
stylesheets ------- 静态的样式表文件
routes ------- 路由文件
index.js ------- 默认的路由
users.js ------- 用户相关的路由
views ------- 路由对应的页面
index.ejs ------- 默认的首页
error.ejs ------- 错误页面
app.js ------- 使用中间件,注册路由
package.json ------- 描述文件
3、准备数据库相关文件
大勋在node系列之数据库mongoose的封装中给大家介绍了如何封装mongoose,可以先行查看如何封装,封装的文件夹为sql,如果不想看的,可以直接通过网盘下载该文件夹
将该sql文件放置项目的跟目录下
Patch和Diff算法
网上看了好多的博客和源码教程,感觉很多仔细的地方没有说清,而且在一些复杂的部分加了好多的描述,所以就想自己也写下心得, 方便自己, 方便他人,有兴趣的同学可以关注我的github里面有我之前一些博文 github/193Eric
我们知道的,在数据更改后,会触发getter,然后通过dep.notify()来通知watcher触发update进而更新视图,最终是通过Diff算法来对比新老Vnode的差异,并把差异更新到Dom视图上
Diff
我们知道的,Virtual DOM是一颗树,而diff算法主要把两颗树进行对比,找出之间的差异,来渲染页面
diff 算法是通过同层的树节点进行比较而非对树进行逐层搜索遍历的方式,所以时间复杂度只有 O(n),是一种相当的算法
1.调用patch函数比较Vnode和OldVnode,如果不一样直接return Vnode即将Vnode真实化后替换掉DOM中的节点
2.如果OldVnode和Vnode值得进一步比较则调用patchVnode方法进行进一步比较,分为以下几种情况:
Vnode有子节点,但是OldVnode没有,则将Vnode的子节点真实化后添加到真实DOM上
Vnode没有子节点,但是OldVnode上有,则将真实DOM上相应位置的节点删除掉
Vnode和OldVnode都有文本节点但是内容不一样,则将真实DOM上的文本节点替换为Vnode上的文本节点
Vnode和OldVnode上都有子节点,需要调用updateChildren函数进一步比较
updateChildren比较规则
提取出Vnode和OldVnode中的子节点分别为vCh和OldCh,并且提出各自的起始和结尾变量标记为 oldS oldE newS newE
如果是oldS和newE匹配上了,那么真实dom中的第一个节点会移到最后
如果是oldE和newS匹配上了,那么真实dom中的最后一个节点会移到最前,匹配上的两个指针向中间移动
如果都没有,在没有key的情况下直接在DOM的oldS位置的前面添加newS,同时newS+1。在有key的情况下会将newS和Oldch上的所有节点对比,如果有相同的则移动DOM并且将旧节点中这个位置置为null且newS+1。如果还没有则直接在DOM的oldS位置的前面添加newS且newS+1
直到出现任意一方的start>end,则有一方遍历结束,整个比较也结束
updateChildren例子:
假设:
真实dom为 A、B、C
oldDom为 A1、B1、C1
newDom为 A2、D2、C2、B2
先确定oldS = A1 ; oldE = C1; newS = A2; newE = B2
先对比oldS和newS,oldE和newE,发现oldS = newS 所以真实dom的A固定不动。排序为 A、B、C
然后oldS = B1 ; oldE = C1; newS = D2; newE = B2
对比发现 oldS = newE , 所以真实dom,B要插入到后面去
真实dom排序为:A、C、B
然后oldS = C1; oldE = C1; newS = D2; newE = B2
对比发现两两都不对等,所以走第三步。
假设有key存在,所以newS直接和oldDom里面的所有节点对比,发现没有存在,然后插入到oldS前面,且newS+1
真实dom排序为:A、D、C、B
然后重新开始,oldS++ > oldE-- ,结束了。
这就是updateChildren,之后就是一直遍历,运行updateChildren直到没有
1, JavaScript的Ajax
Ajax的全称是Asynchronous JavaScript and XML,意思就是用JavaScript执行异步网络请求,而不需要重载(刷新)整个页面。
Ajax使用XMLHttpRequest对象取得新数据,然后再通过 DOM 将新数据插入到页面中。另外,虽然名字中包含 XML 的成分,但 Ajax 通信与数据格式无关; 这种技术就是无须刷新页面即可从服务器取得数据,但不一定是 XML 数据。
对于IE7+和其他浏览器,可以直接使用XMLHttpRequest对象,对于IE6及以前的浏览器,使用ActiveXObject对象。
使用方法如下:
var xhr;
if (window.XMLHttpRequest) {
xhr = new XMLHttpRequest();
} else {
xhr = new ActiveXObject('Microsoft.XMLHTTP');
}
1
2
3
4
5
6
启动请求:
xhr.open(method, url, boolean);
xhr.send();
1
2
注:
1,xhr.open参数含义:
method:请求方式,post、get等
url: 请求链接,只能向同源的url发送请求
boolean:是否异步请求,true:异步, false: 同步,默认为true
2,调用 open()方法并不会真正发送请求, 而只是启动一个请求以备发送。
3,send()方法接收一个参数,即要作为请求主体发送的数据(post方法会使用,get方法直接传null)。如果不需要通过请求主体发送数据,则必须传入 null,因为这个参数对有些浏览器来说是必需的。调用send()之后,请求就会被分派到服务器。
XMLHttpRequest对象的异步请求示例如下:
function success(text) {
console.log(text);
}
function fail(code) {
console.log(code);
}
var xhr = new XMLHttpRequest(); // 新建XMLHttpRequest对象
xhr.onreadystatechange = function () {
// 状态发生变化时,函数被回调
if (xhr.readyState === 4) { // 成功完成
// 判断响应结果:
if (xhr.status === 200) {
// 成功,通过responseText拿到响应的文本:
return success(xhr.responseText);
} else {
// 失败,根据响应码判断失败原因:
return fail(xhr.status);
}
} else {
// HTTP请求还在继续...
}
}
// 发送请求:
xhr.open('get', '/api/categories');
xhr.send(null);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
xhr的属性含义如下:
responseText: 作为响应主体被返回的文本。
responseXML: 如果响应的内容类型是"text/xml"或"application/xml",这个属性中将保存响应数据的 XML DOM 文档。
status: 响应的 HTTP 状态。
statusText: HTTP 状态的说明。
readyState :表示请求/响应过程的当前活动阶段。可取值如下。
0: 未初始化。尚未调用 open()方法。
1: 启动。已经调用 open()方法,但尚未调用 send()方法。
2: 发送。已经调用 send()方法,但尚未接收到响应。
3: 接收。已经接收到部分响应数据。
4: 完成。已经接收到全部响应数据,而且已经可以在客户端使用了。
只要 readyState 属性的值由一个值变成另一个值,都会触发一次 readystatechange 事件。可以利用这个事件来检测每次状态变化后readyState 的值。通常,我们只对 readyState 值为 4 的阶段感兴趣,因为这时所有数据都已经就绪。不过,必须在调用 open()之前指定 onreadystatechange事件处理程序才能确保跨浏览器兼容性。
另外,在接收到响应之前还可以调用 abort()方法来取消异步请求:
xhr.abort();
1
调用这个方法后,XHR 对象会停止触发事件,而且也不再允许访问任何与响应有关的对象属性。在终止请求之后,还应该对 XHR 对象进行解引用操作。由于内存原因,不建议重用 XHR 对象。
2, jQuery的Ajax
$.ajax({
url:"",
type:"GET",
contentType: '',
async:true,
data:{},
dataType:"",
success: function(){
}
});
1
2
3
4
5
6
7
8
9
10
url 必填项,规定把请求发送到哪个 URL。
type 以什么样的方式获取数据,是get或post
contentType:发送POST请求的格式,默认值为’application/x-www-form-urlencoded;
charset=UTF-8’,也可以指定为text/plain、application/json
async 是否异步执行AJAX请求,默认为true,千万不要指定为false
data 发送的数据,可以是字符串、数组或object。如果是GET请求,data将被转换成query附加到URL上,如果是POST请求,根据contentType把data序列化成合适的格式;
dataType
接收的数据格式,可以指定为’html’、‘xml’、‘json’、'text’等,缺省情况下根据响应的Content-Type猜测。
success 可选。执行成功时返回的数据。
缺点:
是基于XHR原生开发的,目前已有的fetch可替代。本身是针对mvc的编程模式,不太适合目前mvvm的编程模式。jQuery本身比较大,如果单纯的使用ajax可以自己封装一个,不然会影响性能体验。
3,Axios
Vue2.0之后,axios开始受到更多的欢迎。其实axios也是对原生XHR的一种封装,不过是Promise实现版本。它是一个用于浏览器和 nodejs 的 HTTP 客户端,符合的ES规范。
axios具有以下特征:
从浏览器中创建 XMLHttpRequest
支持 Promise API
客户端支持防止CSRF
提供了一些并发请求的接口
从 node.js 创建 http 请求
拦截请求和响应
转换请求和响应数据
取消请求
自动转换JSON数据
PS:防止CSRF:就是让你的每个请求都带一个从cookie中拿到的key, 根据浏览器同源策略,假冒的网站是拿不到你cookie中得key的,这样,后台就可以轻松辨别出这个请求是否是用户在假冒网站上的误导输入,从而采取正确的策略。
设置全局的 axios 默认值
axios.defaults.baseURL = 'https://api.example.com';
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'
1
2
3
注:axios 的 headers的 content-type 默认是 “application/json ”
默认情况下,axios将JavaScript对象序列化为JSON,如果是get请求,对请求参数不用做任何处理,但是如果是post请求,并且Content-Type 为application/x-www-form-urlencoded,需要使用URLSearchParams API格式化请求参数, 否则Content-Type依然是application/json
var params = new URLSearchParams();
params.append('param1', 'value1');
params.append('param2', 'value2');
1
2
3
get请求,以下3中写法完全等价
axios.get('/user?id=12345&name=xiaoming')
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
1
2
3
4
5
6
7
axios.get('/user', {
params: {
id: '12345',
name: 'xiaoming'
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
1
2
3
4
5
6
7
8
9
10
11
12
axios({
url: '/user',
method: 'get',
params: {
id: '12345',
name: 'xiaoming'
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
post请求,以下2种写法完全等价
axios({
url: '/user',
method: 'post',
headers: {
'Content-Type': 'application/json'
},
data: {
id: '12345',
name: 'xiaoming'
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var url = '/user';
var data = {
id: '12345',
name: 'xiaoming'
};
axios.post(url, data, {
headers: {
'Content-Type': 'application/json'
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
执行多个并发请求
function getUserAccount() {
return axios.get('/user/12345');
}
function getUserPermissions() {
return axios.get('/user/12345/permissions');
}
axios.all([getUserAccount(), getUserPermissions()])
.then(axios.spread(function (acct, perms) {
// 两个请求现在都执行完成
}));
1
2
3
4
5
6
7
8
9
10
11
12
创建实例
可以使用自定义配置新建一个 axios 实例
axios.create([config])
var instance = axios.create({
baseURL: 'https://some-domain.com/api/',
timeout: 1000,
headers: {'X-Custom-Header': 'foobar'}
});
1
2
3
4
5
6
配置会以一个优先顺序进行合并,顺序由低到高为
1,在 node_modules/axios/lib/defaults.js 找到的库的默认值
2,实例的 defaults 属性
3,请求的 config 参数
// 使用由库提供的配置的默认值来创建实例
// 此时超时配置的默认值是 0
var instance = axios.create();
// 覆写库的超时默认值
// 现在,所有请求都会等待 2.5 秒
instance.defaults.timeout = 2500;
// 为已知需要花费很长时间的请求覆写超时设置
instance.get('/longRequest', {
timeout: 5000
});
1
2
3
4
5
6
7
8
9
10
11
12
拦截器
在请求发出之前或响应被 then 或 catch 处理前拦截它们做预处理。
// 添加请求拦截器
axios.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
}, function (error) {
// 对请求错误做些什么
});
// 添加响应拦截器
axios.interceptors.response.use(function (response) {
// 对响应数据做点什么
}, function (error) {
// 对响应错误做点什么
});
1
2
3
4
5
6
7
8
9
10
11
12
13
可以在稍后移除拦截器:
var myInterceptor = axios.interceptors.request.use(function () {/.../});
axios.interceptors.request.eject(myInterceptor);
1
2
可以为自定义 axios 实例添加拦截器
var instance = axios.create();
instance.interceptors.request.use(function () {/.../});
1
2
4, fetch
window 自带了 window.fetch 方法, 在版的 Firefox 和 Chrome 中已经提供支持,其他浏览器还有兼容性问题,要做兼容性处理。fetch 是一个 基于promise设计的low-level API,不是ajax的进一步封装,而是原生js,它注定不会像你习惯的 $.ajax 或是 axios 等库帮你封装各种各样的功能或实现.
interface GlobalFetch {
fetch(input: RequestInfo, init?: RequestInit): Promise<Response>;
}
1
2
3
fetch()是一个全局的函数,返回一个promise对象。它有两个参数,第一个参数是请求的地址,第二个参数是可选,RequestInit是个对象格式如下:
interface RequestInit {
body?: any;
cache?: RequestCache;
credentials?: RequestCredentials;
headers?: HeadersInit;
integrity?: string;
keepalive?: boolean;
method?: string;
mode?: RequestMode;
redirect?: RequestRedirect;
referrer?: string;
referrerPolicy?: ReferrerPolicy;
window?: any;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
优点:
符合关注分离,没有将输入、输出和用事件来跟踪的状态混杂在一个对象里
更好更方便的写法
更加底层,提供的API丰富
脱离了XHR,是ES规范里新的实现方式
fetch中可以设置mode为"no-cors"(不跨域)
缺点:
fetch不支持同步请求
fetch只对网络请求报错,对400,500都当做成功的请求,需要封装去处理
fetch默认不会带cookie,需要添加配置项
fetch不支持abort,不支持超时控制,使用setTimeout及Promise.reject的实现的超时控制并不能阻止请求过程继续在后台运行,造成了流量的浪费
fetch没有办法原生监测请求的进度,而XHR可以
fetch的使用示例:
window.fetch(url)
.then(response => {
if (response.ok) {
//通过 response 原型上的 json 方法将 response.body 转换为 JS 对象,再返回出去
return response.json();
}
}
).then(result => {
console.log(result);
}).catch(error => {
console.log(error);
})
1
2
3
4
5
6
7
8
9
10
11
12
需要注意以下几点:
1,用 response.ok判断fetch请求是否成功
2,服务端只返回了response对象,而真正的请求结果,即 response.body,则是一个 ReadableStream。fetch 将 response.body 设计成 ReadableStream 在请求大体积文件时变得非常有用。然而,在我们的日常使用中,还是短小的 JSON 片段更加常见。而为了兼容不常见的设计,我们不得不多一次 response.json() 的调用。不仅是调用变得麻烦,如果你的服务端采用了严格的 REST 风格,对于某些特殊情况并没有返回 JSON 字符串,而是用了 HTTP 状态码(如:204 No Content),那么在调用 response.json() 时则会抛出异常。
3,Response 限制了响应内容的重复读取和转换,response .json / response.text 方法只能使用一个并且只能使用一次,同时使用两个,或使用两次都会报如下错误:
Uncaught (in promise) TypeError: Failed to execute 'json' on 'Response': body stream is locked
1
为什么不能使用两次?
因为数据流只能读取一次,一旦读取,数据流变空,再次读取会报错。
————————————————
版权声明:本文为CSDN博主「Sherry慈」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_41480546/article/details/102805864
这里我截取的是一个图片,实际上是会动的。废话不多说,上代码。
HTML:
<canvas id="canvas"></canvas>
1
CSS:
/css reset /
body,p,div,ol,ul,li,dl,dt,dd,h1,h2,h3,h4,h5,h6,form,input,iframe,nav {
margin: 0;
padding: 0;
}
html,body {
width: 100%;
height: 100%;
}
body {
font: 14px Microsoft YaHei;
-webkit-text-size-adjust:100%;
-moz-user-select: none;
-webkit-user-select: none;
user-select: none;
position: relative;
background: #000;
}
width: 100%;
height: 100%;
display: block;
opacity: .8;
}
// 音量大小,0.01-1
//宇宙
var canvas = document.getElementById('canvas'),
ctx = canvas.getContext('2d'),
w = canvas.width = window.innerWidth,
h = canvas.height = window.innerHeight,
hue = 217,
stars = [],
count = 0,
maxStars = 1100; //星星数量,默认1300
var canvas2 = document.createElement('canvas'),
ctx2 = canvas2.getContext('2d');
canvas2.width = 100;
canvas2.height = 100;
var half = canvas2.width / 2,
gradient2 = ctx2.createRadialGradient(half, half, 0, half, half, half);
gradient2.addColorStop(0.025, '#CCC');
gradient2.addColorStop(0.1, 'hsl(' + hue + ', 61%, 33%)');
gradient2.addColorStop(0.25, 'hsl(' + hue + ', 64%, 6%)');
gradient2.addColorStop(1, 'transparent');
ctx2.fillStyle = gradient2;
ctx2.beginPath();
ctx2.arc(half, half, half, 0, Math.PI 2);
ctx2.fill();
// End cache
function random(min, max) {
if (arguments.length < 2) {
max = min;
min = 0;
}
if (min > max) {
var hold = max;
max = min;
min = hold;
}
return Math.floor(Math.random() (max - min + 1)) + min;
}
function maxOrbit(x, y) {
var max = Math.max(x, y),
diameter = Math.round(Math.sqrt(max max + max max));
return diameter / 2;
//星星移动范围,值越大范围越小,
}
var Star = function() {
this.orbitRadius = random(maxOrbit(w, h));
this.radius = random(60, this.orbitRadius) / 10; //星星大小,值越大星星越小,默认8
this.orbitX = w / 2;
this.orbitY = h / 2;
this.timePassed = random(0, maxStars);
this.speed = random(this.orbitRadius) / 80000; //星星移动速度,值越大越慢,默认5W
this.alpha = random(2, 10) / 10;
count++;
stars[count] = this;
}
Star.prototype.draw = function() {
var x = Math.sin(this.timePassed) this.orbitRadius + this.orbitX,
y = Math.cos(this.timePassed) this.orbitRadius + this.orbitY,
twinkle = random(10);
if (twinkle === 1 && this.alpha > 0) {
this.alpha -= 0.05;
} else if (twinkle === 2 && this.alpha < 1) {
this.alpha += 0.05;
}
ctx.globalAlpha = this.alpha;
ctx.drawImage(canvas2, x - this.radius / 2, y - this.radius / 2, this.radius, this.radius);
this.timePassed += this.speed;
}
for (var i = 0; i < maxStars; i++) {
new Star();
}
function animation() {
ctx.globalCompositeOperation = 'source-over';
ctx.globalAlpha = 0.5; //尾巴
ctx.fillStyle = 'hsla(' + hue + ', 64%, 6%, 2)';
ctx.fillRect(0, 0, w, h)
ctx.globalCompositeOperation = 'lighter';
for (var i = 1,
l = stars.length; i < l; i++) {
stars[i].draw();
};
window.requestAnimationFrame(animation);
}
animation();
蓝蓝设计( www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 、平面设计服务。
开发工具与关键技术:Adobe Dreamweaver CC
作者:黄灿
撰写时间:2019.1.16
在模仿华为官方网页的练习当中我发现华为官网中有一个遮罩层是随便点击遮罩层的背景也能关闭掉遮罩层,但唯独点击内容区域不会关闭掉遮罩层。于是我开始模仿这个写案例,连内容也一模一样(因为这个练习就是要写出和华为关一样的效果或则比它更好的效果),一开始我是这样子写的(图1)
图1
class=Select_Region_bj 我给了一个灰色半透明的背景样式,后来在Javascript中写onclick事件无论这么写,点击内容区也是会关闭掉遮罩层。我百思不得其解到底怎么样写才能点击内容区不会关闭遮罩层,后来下课期间我看见我同学他写的带能点击内容区不会关闭遮罩层。我问他你是这么写的,他告诉我:“把他们分离就可以的了。”我思考了一会,脑补:分离?怎么分离?补着补着补着就补出了背景和内容区分离。分离写(图2)
图2
把背景层和内容区分开来写,不要在背景层中包裹内容,这样子点击内容区就不会关闭掉遮罩层了!
蓝蓝设计( www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 、平面设计服务。
vue中关于插槽的文档说明很短,语言又写的很凝练,再加上其和methods,data,computed等常用选项使用频率、使用先后上的差别,这就有可能造成初次接触插槽的开发者容易产生“算了吧,回头再学,反正已经可以写基础组件了”,于是就关闭了vue说明文档。
实际上,插槽的概念很简单,下面通过分三部分来讲。这个部分也是按照vue说明文档的顺序来写的。
进入三部分之前,先让还没接触过插槽的同学对什么是插槽有一个简单的概念:插槽,也就是slot,是组件的一块HTML模板,这块模板显示不显示、以及怎样显示由父组件来决定。 实际上,一个slot最核心的两个问题这里就点出来了,是显示不显示和怎样显示。
由于插槽是一块模板,所以,对于任何一个组件,从模板种类的角度来分,其实都可以分为非插槽模板和插槽模板两大类。
非插槽模板指的是html模板,指的是‘div、span、ul、table’这些,非插槽模板的显示与隐藏以及怎样显示由插件自身控制;插槽模板是slot,它是一个空壳子,因为它显示与隐藏以及最后用什么样的html模板显示由父组件控制。但是插槽显示的位置确由子组件自身决定,slot写在组件template的哪块,父组件传过来的模板将来就显示在哪块。
首先是单个插槽,单个插槽是vue的官方叫法,但是其实也可以叫它默认插槽,或者与具名插槽相对,我们可以叫它匿名插槽。因为它不用设置name属性。
单个插槽可以放置在组件的任意位置,但是就像它的名字一样,一个组件中只能有一个该类插槽。相对应的,具名插槽就可以有很多个,只要名字(name属性)不同就可以了。
下面通过一个例子来展示。
父组件:
-
<template>
-
<div class="father">
-
<h3>这里是父组件</h3>
-
<child>
-
<div class="tmpl">
-
<span>菜单1</span>
-
<span>菜单2</span>
-
<span>菜单3</span>
-
<span>菜单4</span>
-
<span>菜单5</span>
-
<span>菜单6</span>
-
</div>
-
</child>
-
</div>
-
</template>
子组件:
-
<template>
-
<div class="child">
-
<h3>这里是子组件</h3>
-
<slot></slot>
-
</div>
-
</template>
在这个例子里,因为父组件在<child></child>里面写了html模板,那么子组件的匿名插槽这块模板就是下面这样。也就是说,子组件的匿名插槽被使用了,是被下面这块模板使用了。
-
<div class="tmpl">
-
<span>菜单1</span>
-
<span>菜单2</span>
-
<span>菜单3</span>
-
<span>菜单4</span>
-
<span>菜单5</span>
-
<span>菜单6</span>
-
</div>
最终的渲染结果如图所示:
-
-
注:所有demo都加了样式,以方便观察。其中,父组件以灰色背景填充,子组件都以浅蓝色填充。
匿名插槽没有name属性,所以是匿名插槽,那么,插槽加了name属性,就变成了具名插槽。具名插槽可以在一个组件中出现N次。出现在不同的位置。下面的例子,就是一个有两个具名插槽和单个插槽的组件,这三个插槽被父组件用同一套css样式显示了出来,不同的是内容上略有区别。
父组件:
-
<template>
-
<div class="father">
-
<h3>这里是父组件</h3>
-
<child>
-
<div class="tmpl" slot="up">
-
<span>菜单1</span>
-
<span>菜单2</span>
-
<span>菜单3</span>
-
<span>菜单4</span>
-
<span>菜单5</span>
-
<span>菜单6</span>
-
</div>
-
<div class="tmpl" slot="down">
-
<span>菜单-1</span>
-
<span>菜单-2</span>
-
<span>菜单-3</span>
-
<span>菜单-4</span>
-
<span>菜单-5</span>
-
<span>菜单-6</span>
-
</div>
-
<div class="tmpl">
-
<span>菜单->1</span>
-
<span>菜单->2</span>
-
<span>菜单->3</span>
-
<span>菜单->4</span>
-
<span>菜单->5</span>
-
<span>菜单->6</span>
-
</div>
-
</child>
-
</div>
-
</template>
子组件:
-
<template>
-
<div class="child">
-
// 具名插槽
-
<slot name="up"></slot>
-
<h3>这里是子组件</h3>
-
// 具名插槽
-
<slot name="down"></slot>
-
// 匿名插槽
-
<slot></slot>
-
</div>
-
</template>
显示结果如图:
可以看到,父组件通过html模板上的slot属性关联具名插槽。没有slot属性的html模板默认关联匿名插槽。
最后,就是我们的作用域插槽。这个稍微难理解一点。官方叫它作用域插槽,实际上,对比前面两种插槽,我们可以叫它带数据的插槽。什么意思呢,就是前面两种,都是在组件的template里面写
-
匿名插槽
-
<slot></slot>
-
具名插槽
-
<slot name="up"></slot>
但是作用域插槽要求,在slot上面绑定数据。也就是你得写成大概下面这个样子。
-
<slot name="up" :data="data"></slot>
-
export default {
-
data: function(){
-
return {
-
data: ['zhangsan','lisi','wanwu','zhaoliu','tianqi','xiaoba']
-
}
-
},
-
}
我们前面说了,插槽最后显示不显示是看父组件有没有在child下面写模板,像下面那样。
-
<child>
-
html模板
-
</child>
写了,插槽就总得在浏览器上显示点东西,东西就是html该有的模样,没写,插槽就是空壳子,啥都没有。
OK,我们说有html模板的情况,就是父组件会往子组件插模板的情况,那到底插一套什么样的样式呢,这由父组件的html+css共同决定,但是这套样式里面的内容呢?
正因为作用域插槽绑定了一套数据,父组件可以拿来用。于是,情况就变成了这样:样式父组件说了算,但内容可以显示子组件插槽绑定的。
我们再来对比,作用域插槽和单个插槽和具名插槽的区别,因为单个插槽和具名插槽不绑定数据,所以父组件是提供的模板要既包括样式由包括内容的,上面的例子中,你看到的文字,“菜单1”,“菜单2”都是父组件自己提供的内容;而作用域插槽,父组件只需要提供一套样式(在确实用作用域插槽绑定的数据的前提下)。
下面的例子,你就能看到,父组件提供了三种样式(分别是flex、ul、直接显示),都没有提供数据,数据使用的都是子组件插槽自己绑定的那个人名数组。
父组件:
-
<template>
-
<div class="father">
-
<h3>这里是父组件</h3>
-
<!--第一次使用:用flex展示数据-->
-
<child>
-
<template slot-scope="user">
-
<div class="tmpl">
-
<span v-for="item in user.data">{{item}}</span>
-
</div>
-
</template>
-
-
</child>
-
-
<!--第二次使用:用列表展示数据-->
-
<child>
-
<template slot-scope="user">
-
<ul>
-
<li v-for="item in user.data">{{item}}</li>
-
</ul>
-
</template>
-
-
</child>
-
-
<!--第三次使用:直接显示数据-->
-
<child>
-
<template slot-scope="user">
-
{{user.data}}
-
</template>
-
-
</child>
-
-
<!--第四次使用:不使用其提供的数据, 作用域插槽退变成匿名插槽-->
-
<child>
-
我就是模板
-
</child>
-
</div>
-
</template>
子组件:
-
<template>
-
<div class="child">
-
-
<h3>这里是子组件</h3>
-
// 作用域插槽
-
<slot :data="data"></slot>
-
</div>
-
</template>
-
-
export default {
-
data: function(){
-
return {
-
data: ['zhangsan','lisi','wanwu','zhaoliu','tianqi','xiaoba']
-
}
-
}
-
}
结果如图所示:
以上三个demo就放在GitHub了,有需要的可以去取。使用非常方便,是基于vue-cli搭建工程。
蓝蓝设计( www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 、平面设计服务。
蓝蓝设计的小编 http://www.lanlanwork.com