首页

网页制作如何引入图标

seo达人

HTML中如何引入图标



在头部引用在HTML中里面引用一个东西,。在头部引用<script defer src="https://use.fontawesome.com/releases/v5.0.8/js/all.js"&gt;&lt;/script&gt;

然后在网上搜索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.完美是最无情的禁锢


js reduce()

seo达人

是什么

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()的应用

  1. 数组扁平化

    递归+reduce



    let arr = [1, 2, '3js', [4, 5, [6], [7, 8, [9, 10, 11], null, 'abc'], {age: 12}, [13, 14]], '[]'];



    function flatten(arr) {

      if(Array.isArray(arr)) {

        return arr.reduce((prev, cur) => {

           // 如果遍历的当前项是数组,递归调用flatten

          return Array.isArray(cur) ? prev.concat(flatten(cur)) : prev.concat(cur)

        }, [])

      } else {

        throw new Error(' 当前参数不是数组')

      }

    }

    console.log(flatten(arr));



    PS:这里的throw new Error只是用来判断一开始的arr,这是因为在递归只传入数组。


node系列之数据接口注册接口的实现(token验证登陆)

seo达人

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文件放置项目的跟目录下


  • myapp

    - sql

    - collection

    users.js

    db.js

    index.js



    4、编写注册接口

    目标文件: myapp/routes/users.js



    实现思路:使用post提交数据的方式,先以手机号查询有没有该用户,如果有该用户,提示用户该账号已经注册过了;如果没有该用户,则可以完成注册,首先得将密码加密,加密完成后插入数据库



    代码实现:



    // 找到用户集合

    var User = require('./../sql/collection/users');

    // 找到数据库封装文件

    var sql = require('./../sql');

    // 状态码的封装

    var utils = require('./../utils')

    // 用户唯一标识的id

    var uuid = require('node-uuid');

    // 密码加密模块

    var bcrypt = require('bcryptjs');

    var salt = bcrypt.genSaltSync(10); // 加密级别



    // 实现注册接口 -- post提交方式

    router.post('/register', (req, res, next) => {

      // 1、先获取表单信息

      let { username, password, tel } = req.body;

      // 2、根据手机号查询 用户集合中是否有该用户,如果有,返回有该账户,如果没有注册继续

      sql.find(User, { tel }, { id: 0 }).then(data => {

        // 2.1 判断有没有该用户

        if (data.length === 0) {

          // 2.2 没有该用户----继续完成注册操作

          // 2.2.1 生成用户的id

          let userid = 'users
    ' + uuid.v1();

          // 2.2.2 对密码加密

          password = bcrypt.hashSync(password, salt)

          // 2.2.3 插入数据库

          sql.insert(User, { userid, username, password, tel}).then(() => {

            res.send(utils.registersuccess)

          })

        } else {

          // 2.3 已有该用户

          res.send(utils.registered)

        }

      })

    })



    附 状态码封装模块 myapp/utils/index.js

    module.exports = {

      registered: {

        code: '10000',

        message: '该用户已注册,请直接登录' 

      },

      registersuccess: {

        code: '10101',

        message: '注册成功' 

      }

    }



    5、编写登陆接口

    目标文件 myapp/routes/users.js

    实现思路:根据手机号查询有没有该用户,如果没有,提示用户未注册,如果有该用户,使用bcryptjs模块验证密码的有效性,如果有效,生成token,返回给前端相应的token值。

    var jwt = require('jsonwebtoken');

    // 实现登陆功能

    router.post('/login', (req, res, next) => {

      // 1、获取表单信息

      let { tel, password } = req.body;

      // 2、依据手机号查询有没有该用户

      sql.find(User, { tel }, { _id: 0 }).then(data => {

        // 2.1 判断有么有该用户

        if (data.length === 0) {

          // 2.2 没有该用户

          res.send(utils.unregister)

        } else {

          // 2.3 有该用户,验证密码

          // 2.3.1 获取数据库中的密码

          let pwd = data[0].password;

          // 2.3.2 比较 输入的 密码和数据库中的密码

          var flag = bcrypt.compareSync(password, pwd) // 前为输入,后为数据库

          if (flag) {

            // 2.3.3 密码正确,生成token

            let userid = data[0].userid

            let username = data[0].username

            let token = jwt.sign({ userid, username }, 'daxunxun', {

              expiresIn: 606024// 授权时效24小时

            })

            res.send({

              code: '10010',

              message: '登陆成功',

              token: token

            })

          } else {

            // 2.3.4 密码错误

            res.send({

              code: '10100',

              message: '密码错误'

            })

          }

        }

      })

    })



    6、验证登陆实现

    目标文件: myapp/app.js

    实现思路:很多的数据请求都需要登陆之后才能获取到,在此统一封装验证登陆

    // 引入token模块

    var jwt = require('jsonwebtoken');

    // 全局的路由匹配

    app.use((req, res, next) => {

     // 排除登陆注册页面

      if (req.url !== '/users/login' && req.url !== '/users/register') {

      // 不同形式获取token值

        let token = req.headers.token || req.query.token || req.body.token;

        // 如果存在token ---- 验证

        if (token) {

          jwt.verify(token, 'daxunxun', function(err, decoded) {

            if (err) {

              res.send({ 

                code: '10119', 

                message: '没有找到token.' 

              });

            } else {

              req.decoded = decoded;  

              console.log('验证成功', decoded);

              next()

            }

          }) 

        } else { // 不存在 - 告诉用户---意味着未登录

          res.send({ 

            code: '10119', 

            message: '没有找到token.' 

          });

        }

      } else {

        next()

      }

    })




Vue源码剖析(三)patch和Diff算法

seo达人

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直到没有


一文秒懂 ajax, fetch, axios

seo达人

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

JS实现动态星空背景

seo达人



这里我截取的是一个图片,实际上是会动的。废话不多说,上代码。

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;

}


canvas {

    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界面设计  包装设计  图标定制  用户体验 、交互设计、 网站建设 平面设计服务

点击遮罩层的背景关闭遮罩层

seo达人

开发工具与关键技术:Adobe Dreamweaver CC

作者:黄灿

撰写时间:2019.1.16



在模仿华为官方网页的练习当中我发现华为官网中有一个遮罩层是随便点击遮罩层的背景也能关闭掉遮罩层,但唯独点击内容区域不会关闭掉遮罩层。于是我开始模仿这个写案例,连内容也一模一样(因为这个练习就是要写出和华为关一样的效果或则比它更好的效果),一开始我是这样子写的(图1)



图1



class=Select_Region_bj 我给了一个灰色半透明的背景样式,后来在Javascript中写onclick事件无论这么写,点击内容区也是会关闭掉遮罩层。我百思不得其解到底怎么样写才能点击内容区不会关闭遮罩层,后来下课期间我看见我同学他写的带能点击内容区不会关闭遮罩层。我问他你是这么写的,他告诉我:“把他们分离就可以的了。”我思考了一会,脑补:分离?怎么分离?补着补着补着就补出了背景和内容区分离。分离写(图2)

图2



把背景层和内容区分开来写,不要在背景层中包裹内容,这样子点击内容区就不会关闭掉遮罩层了!

蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计  cs界面设计  ipad界面设计  包装设计  图标定制  用户体验 、交互设计、 网站建设 平面设计服务

深入理解vue中的slot与slot-scope

seo达人

写在前面

vue中关于插槽的文档说明很短,语言又写的很凝练,再加上其和methods,data,computed等常用选项使用频率、使用先后上的差别,这就有可能造成初次接触插槽的开发者容易产生“算了吧,回头再学,反正已经可以写基础组件了”,于是就关闭了vue说明文档。

实际上,插槽的概念很简单,下面通过分三部分来讲。这个部分也是按照vue说明文档的顺序来写的。

进入三部分之前,先让还没接触过插槽的同学对什么是插槽有一个简单的概念:插槽,也就是slot,是组件的一块HTML模板,这块模板显示不显示、以及怎样显示由父组件来决定。 实际上,一个slot最核心的两个问题这里就点出来了,是显示不显示怎样显示

由于插槽是一块模板,所以,对于任何一个组件,从模板种类的角度来分,其实都可以分为非插槽模板插槽模板两大类。
非插槽模板指的是html模板,指的是‘div、span、ul、table’这些,非插槽模板的显示与隐藏以及怎样显示由插件自身控制;插槽模板是slot,它是一个空壳子,因为它显示与隐藏以及最后用什么样的html模板显示由父组件控制。但是插槽显示的位置确由子组件自身决定,slot写在组件template的哪块,父组件传过来的模板将来就显示在哪块

单个插槽 | 默认插槽 | 匿名插槽

首先是单个插槽,单个插槽是vue的官方叫法,但是其实也可以叫它默认插槽,或者与具名插槽相对,我们可以叫它匿名插槽。因为它不用设置name属性。

单个插槽可以放置在组件的任意位置,但是就像它的名字一样,一个组件中只能有一个该类插槽。相对应的,具名插槽就可以有很多个,只要名字(name属性)不同就可以了。

下面通过一个例子来展示。

父组件:


  1. <template>
  2. <div class="father">
  3. <h3>这里是父组件</h3>
  4. <child>
  5. <div class="tmpl">
  6. <span>菜单1</span>
  7. <span>菜单2</span>
  8. <span>菜单3</span>
  9. <span>菜单4</span>
  10. <span>菜单5</span>
  11. <span>菜单6</span>
  12. </div>
  13. </child>
  14. </div>
  15. </template>

子组件:


  1. <template>
  2. <div class="child">
  3. <h3>这里是子组件</h3>
  4. <slot></slot>
  5. </div>
  6. </template>

在这个例子里,因为父组件在<child></child>里面写了html模板,那么子组件的匿名插槽这块模板就是下面这样。也就是说,子组件的匿名插槽被使用了,是被下面这块模板使用了。


  1. <div class="tmpl">
  2. <span>菜单1</span>
  3. <span>菜单2</span>
  4. <span>菜单3</span>
  5. <span>菜单4</span>
  6. <span>菜单5</span>
  7. <span>菜单6</span>
  8. </div>

最终的渲染结果如图所示:



  1. 注:所有demo都加了样式,以方便观察。其中,父组件以灰色背景填充,子组件都以浅蓝色填充。

具名插槽

匿名插槽没有name属性,所以是匿名插槽,那么,插槽加了name属性,就变成了具名插槽。具名插槽可以在一个组件中出现N次。出现在不同的位置。下面的例子,就是一个有两个具名插槽单个插槽的组件,这三个插槽被父组件用同一套css样式显示了出来,不同的是内容上略有区别。

父组件:


  1. <template>
  2. <div class="father">
  3. <h3>这里是父组件</h3>
  4. <child>
  5. <div class="tmpl" slot="up">
  6. <span>菜单1</span>
  7. <span>菜单2</span>
  8. <span>菜单3</span>
  9. <span>菜单4</span>
  10. <span>菜单5</span>
  11. <span>菜单6</span>
  12. </div>
  13. <div class="tmpl" slot="down">
  14. <span>菜单-1</span>
  15. <span>菜单-2</span>
  16. <span>菜单-3</span>
  17. <span>菜单-4</span>
  18. <span>菜单-5</span>
  19. <span>菜单-6</span>
  20. </div>
  21. <div class="tmpl">
  22. <span>菜单->1</span>
  23. <span>菜单->2</span>
  24. <span>菜单->3</span>
  25. <span>菜单->4</span>
  26. <span>菜单->5</span>
  27. <span>菜单->6</span>
  28. </div>
  29. </child>
  30. </div>
  31. </template>

子组件:


  1. <template>
  2. <div class="child">
  3. // 具名插槽
  4. <slot name="up"></slot>
  5. <h3>这里是子组件</h3>
  6. // 具名插槽
  7. <slot name="down"></slot>
  8. // 匿名插槽
  9. <slot></slot>
  10. </div>
  11. </template>

显示结果如图:


可以看到,父组件通过html模板上的slot属性关联具名插槽。没有slot属性的html模板默认关联匿名插槽。

作用域插槽 | 带数据的插槽

最后,就是我们的作用域插槽。这个稍微难理解一点。官方叫它作用域插槽,实际上,对比前面两种插槽,我们可以叫它带数据的插槽。什么意思呢,就是前面两种,都是在组件的template里面写


  1. 匿名插槽
  2. <slot></slot>
  3. 具名插槽
  4. <slot name="up"></slot>

但是作用域插槽要求,在slot上面绑定数据。也就是你得写成大概下面这个样子。


  1. <slot name="up" :data="data"></slot>
  2. export default {
  3. data: function(){
  4. return {
  5. data: ['zhangsan','lisi','wanwu','zhaoliu','tianqi','xiaoba']
  6. }
  7. },
  8. }

我们前面说了,插槽最后显示不显示是看父组件有没有在child下面写模板,像下面那样。


  1. <child>
  2. html模板
  3. </child>

写了,插槽就总得在浏览器上显示点东西,东西就是html该有的模样,没写,插槽就是空壳子,啥都没有。
OK,我们说有html模板的情况,就是父组件会往子组件插模板的情况,那到底插一套什么样的样式呢,这由父组件的html+css共同决定,但是这套样式里面的内容呢?

正因为作用域插槽绑定了一套数据,父组件可以拿来用。于是,情况就变成了这样:样式父组件说了算,但内容可以显示子组件插槽绑定的。

我们再来对比,作用域插槽和单个插槽和具名插槽的区别,因为单个插槽和具名插槽不绑定数据,所以父组件是提供的模板要既包括样式由包括内容的,上面的例子中,你看到的文字,“菜单1”,“菜单2”都是父组件自己提供的内容;而作用域插槽,父组件只需要提供一套样式(在确实用作用域插槽绑定的数据的前提下)。

下面的例子,你就能看到,父组件提供了三种样式(分别是flex、ul、直接显示),都没有提供数据,数据使用的都是子组件插槽自己绑定的那个人名数组。

父组件:


  1. <template>
  2. <div class="father">
  3. <h3>这里是父组件</h3>
  4. <!--第一次使用:用flex展示数据-->
  5. <child>
  6. <template slot-scope="user">
  7. <div class="tmpl">
  8. <span v-for="item in user.data">{{item}}</span>
  9. </div>
  10. </template>
  11. </child>
  12. <!--第二次使用:用列表展示数据-->
  13. <child>
  14. <template slot-scope="user">
  15. <ul>
  16. <li v-for="item in user.data">{{item}}</li>
  17. </ul>
  18. </template>
  19. </child>
  20. <!--第三次使用:直接显示数据-->
  21. <child>
  22. <template slot-scope="user">
  23. {{user.data}}
  24. </template>
  25. </child>
  26. <!--第四次使用:不使用其提供的数据, 作用域插槽退变成匿名插槽-->
  27. <child>
  28. 我就是模板
  29. </child>
  30. </div>
  31. </template>

子组件:


  1. <template>
  2. <div class="child">
  3. <h3>这里是子组件</h3>
  4. // 作用域插槽
  5. <slot :data="data"></slot>
  6. </div>
  7. </template>
  8. export default {
  9. data: function(){
  10. return {
  11. data: ['zhangsan','lisi','wanwu','zhaoliu','tianqi','xiaoba']
  12. }
  13. }
  14. }

结果如图所示:

github

以上三个demo就放在GitHub了,有需要的可以去取。使用非常方便,是基于vue-cli搭建工程。

蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计  cs界面设计  ipad界面设计  包装设计  图标定制  用户体验 、交互设计、 网站建设 平面设计服务

开发过程中积累的CSS样式(持续更新)

seo达人

前言:平时写页面的时候有些样式使用完发现没过多久就忘记了,这篇文章主要是用来记录开发过程中容易忘记的CSS样式,与其总是去网上查,还不如一个一个记录下来,虽然说之前的都没有记录,但是知识的累积不论什么时候开始做都不会晚的。



首先 记录几个好用的插件网站:



layDate日期与时间组件: https://www.layui.com/laydate/

Vant移动端插件库: https://youzan.github.io/vant/#/zh-CN/intro

Element组件库: https://element.eleme.cn/#/zh-CN/component/installation

Vue.js框架: https://cn.vuejs.org/v2/guide/

Bootstrap框架: https://v2.bootcss.com/index.html

菜鸟教程官网: https://www.runoob.com/

w3school官网: https://www.w3school.com.cn/

下面是遇到的一些想累积的css样式:(内容会随时间累积的越来越多)



1、一个 div 中的内容实现上下滑动效果(而不是超出body的高以后上下滑动):overflow:auto;

简单的描述:body 中的一个 div 内,如果设置了固定的 height,而内容的总 height 超出了 div 的高,则超出的部分就会被覆盖,而想实现超出的部分变成滑动的效果而不被覆盖,则用 overflow:auto; 实现。



2、修改 前端框架封装好的css样式: border-radius: 20px!important;(注意使用英文的 ! 叹号)

简单的描述:在开发过程中经常使用一些前端框架(bootstrap,vue.js,laydate,Vant等等),在使用link导入css文件以后,发现有些css是在标签内使用内嵌的方式实现的,优先级最高,那么我们怎么修改呢?

比如:css文件中的边框弧度样式为10px:border-radius: 10px;

我们想改成20px,则在后面加上 !important 即可:border-radius: 20px!important;



这篇文章主要是以后回头复习或者忘记了的时候给我自己看的,但是如果恰好也帮助到了你,那是更加的有意义,在以后的开发过程中,该文章的内容一定会累积的越来越多

蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计  cs界面设计  ipad界面设计  包装设计  图标定制  用户体验 、交互设计、 网站建设 平面设计服务

JS--普通数字格式与会计金额格式之间的转换

seo达人

普通数字转会计金额格式(保留两位小数)

我们可以用数字的toLocaleString()方法将普通数字转为会计金额格式,但是这种方式无法保留两位小数(四舍五入),如果是整数或者小数长度只有一位的时候无法自动补0



例如:





思路:

利用toLocaleString()以及toFixed()先对数字进行一个转换得到最多保留了2位小数的金额,然后判断数字是为整数还是带有小数,如果带有小数则进行切割,判断小数长度为1时自动补0



// 普通数字转会计金额格式 第一种

    function toThousandsFormates(num) {

        // 判断传进来的数字是否为非空数字

       if (!isNaN(parseFloat(num))) {

            var reg = /./g

            var newNum = Number(Number(num).toFixed(2)).toLocaleString()

            // 判断转换后的数字是否带有小数

            if (reg.test(newNum)) {

                var numArr = newNum.split('.')

                // 判断小数点后数字长度为1,则自动补0

                numArr[1] = numArr[1].length === 1 ? numArr[1] + '0' : numArr[1]

                return numArr.join('.')

            } else {

                // 整数直接在后面补上0.00

                return newNum + '.00'

            }



        } else {

            return ''

        }

    }

    console.log(toThousandsFormates('0')); // 0.00

    console.log(toThousandsFormates('')); // ''

    console.log(toThousandsFormates(966)); // 966.00

    console.log(toThousandsFormates(966.3)); // 966.30

    console.log(toThousandsFormates(9669228.55)); // 9,669,228.55

    console.log(toThousandsFormates(96566.56954)); // 96,566.57



经过查阅资料后,发现toLocaleString()它里面自带属性可以检查到最少保留了几位小数,不够自动补0,这样我们上面的代码其实可以更加简单,如下:

// 普通数字转会计金额格式 第二种

function toThousandsFormates2(num) {

    // 判断传进来的数字是否为非空数字

    if (!isNaN(parseFloat(num))) {

        var newNum = Number(Number(num).toFixed(2)).toLocaleString('zh', { minimumFractionDigits: 2 })

        return newNum



    } else {

        return ''

    }

}



console.log(toThousandsFormates2('0')); // 0.00

console.log(toThousandsFormates2('')); // ''

console.log(toThousandsFormates2(966)); // 966.00

console.log(toThousandsFormates2(966.3)); // 966.30

console.log(toThousandsFormates2(9669228.55)); // 9,669,228.55

console.log(toThousandsFormates2(96566.56954)); // 96,566.57



// 结果一模一样



会计金额格式转普通数字(利用正则)

// 会计金额格式转为普通数字

    function rMoney(num) {

        return parseFloat(num.replace(/[^\d\.-]/g, ''))

    }

    console.log(rMoney('96,566.57')); // 96566.57

    console.log(rMoney('966.30')); // 966.3

    console.log(rMoney('9,669,228.55')); // 9669228.55

蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计  cs界面设计  ipad界面设计  包装设计  图标定制  用户体验 、交互设计、 网站建设 平面设计服务

日历

链接

个人资料

蓝蓝设计的小编 http://www.lanlanwork.com

存档