xiayefeng

xiayefeng 查看完整档案

填写现居城市  |  填写毕业院校  |  填写所在公司/组织填写个人主网站
编辑
_ | |__ _ _ __ _ | '_ \| | | |/ _` | | |_) | |_| | (_| | |_.__/ \__,_|\__, | |___/ 个人简介什么都没有

个人动态

xiayefeng 关注了专栏 · 2020-07-01

民工哥技术之路

公众号:民工哥技术之路、《Linux系统运维指南 从入门到企业实战》作者。专注系统架构、高可用、高性能、高并发,数据库、大数据、数据分析、Python技术、集群中间件、后端等开源技术分享。

关注 26050

xiayefeng 收藏了文章 · 2020-05-21

Nginx入门到实战(1)基础篇

一、环境

服务器版本:CentOS 7.2

为了保证学习阶段不遇到奇怪的事情,请保证以下四点(大神选择性无视)

  1. 确认系统网络
  2. 确认yum可用
  3. 确认关闭iptables
  4. 确认停用selinux
#查看iptables状态
systemctl status firewalld.service
#关闭防火墙(临时关闭)
systemctl stop firewalld.service
#查看SELinux状态 
getenforce
#临时关闭SELinux 
setenforce 0

安装一些系统基本工具,正常情况系统都会自带(没有在装哦)

yum -y install gcc gcc-c++ autoconf pcre pcre-devel make automake
yum -y install wget httpd-tools vim

二、Nginx是什么?

Nginx是一个开源且高性能、可靠的HTTP中间件、代理服务
其他的HTTP服务:

  1. HTTPD-Apache基金会
  2. IIS-微软
  3. GWS-Google(不对外开放)

近几年,Nginx的市场占有率越来越高,一度飙升,为什么呢?接下来我们就知道了!

三、我们为什么选择Nginx?

1. IO多路复用epoll(IO复用)

如何理解呢?举个例子吧!
有A、B、C三个老师,他们都遇到一个难题,要帮助一个班级的学生解决课堂作业。
老师A采用从第一排开始一个学生一个学生轮流解答的方式去回答问题,老师A浪费了很多时间,并且有的学生作业还没有完成呢,老师就来了,反反复复效率极慢。
老师B是一个忍者,他发现老师A的方法行不通,于是他使用了影分身术,分身出好几个自己同一时间去帮好几个同学回答问题,最后还没回答完,老师B消耗光了能量累倒了。
老师C比较精明,他告诉学生,谁完成了作业举手,有举手的同学他才去指导问题,他让学生主动发声,分开了“并发”。
这个老师C就是Nginx。

2. 轻量级

  • 功能模块少 - Nginx仅保留了HTTP需要的模块,其他都用插件的方式,后天添加
  • 代码模块化 - 更适合二次开发,如阿里巴巴Tengine

3. CPU亲和

把CPU核心和Nginx工作进程绑定,把每个worker进程固定在一个CPU上执行,减少切换CPU的cache miss,从而提高性能。

三、安装与目录

本人使用了鸟哥的lnmp集成包 https://lnmp.org,简单方便-推荐!

#执行这句语句,根据指引,将安装 nginx php mysql 可进入lnmp官网查看更详细的过程
#默认安装目录/usr/local
wget -c http://soft.vpser.net/lnmp/lnmp1.4.tar.gz && tar zxf lnmp1.4.tar.gz && cd lnmp1.4 && ./install.sh lnmp

#默认安装目录
/usr/local

四、基本配置

#打开主配置文件,若你是用lnmp环境安装
vim /usr/local/nginx/conf/nginx.conf

----------------------------------------

user                    #设置nginx服务的系统使用用户
worker_processes        #工作进程数 一般情况与CPU核数保持一致
error_log               #nginx的错误日志
pid                     #nginx启动时的pid

events {
    worker_connections    #每个进程允许最大连接数
    use                   #nginx使用的内核模型
}

我们使用 nginx 的 http 服务,在配置文件 nginx.conf 中的 http 区域,配置无数个 server ,每一个 server 对应这一个虚拟主机或者域名

http {
    ... ...        #后面再详细介绍 http 配置项目
    
    server {
        listen 80                          #监听端口;
        server_name localhost              #地址
        
        location / {                       #访问首页路径
            root /xxx/xxx/index.html       #默认目录
            index index.html index.htm     #默认文件 
        }        
        
        error_page  500 504   /50x.html    #当出现以上状态码时从新定义到50x.html        
        location = /50x.html {             #当访问50x.html时
            root /xxx/xxx/html             #50x.html 页面所在位置
        }        
    }
    
    server {
        ... ... 
    } 
}

一个 server 可以出现多个 location ,我们对不同的访问路径进行不同情况的配置
我们再来看看 http 的配置详情

http {
    sendfile  on                  #高效传输文件的模式 一定要开启
    keepalive_timeout   65        #客户端服务端请求超时时间
    log_format  main   XXX        #定义日志格式 代号为main
    access_log  /usr/local/access.log  main     #日志保存地址 格式代码 main
}

四、模块

查看 nginx 已开启和编联进去的模块,模块太多了,就不在这长篇大论,有需要自行百度吧~

#大写V查看所有模块,小写v查看版本
nginx -V
# 查看此配置文件 是否存在语法错误
nginx -tc /usr/local/nginx/conf/nginx.conf
查看原文

xiayefeng 收藏了文章 · 2020-04-26

JS打包工具rollup——完全入门指南

前言

最近在做一个提供给浏览器和node同时使用的js的url模板工具类,在用什么打包工具上纠结了一段时间,正好有一天在知乎上看到了关于rollup的介绍,在自己试了试之后,就决定用rollup.js来打包自己的工具类了。

这篇文章主要是为了让对rollup.js也有兴趣的同学能够快速入门rollup的使用方式而写的,文章除了开始对rollup.js的基本介绍之外,主要用多个demo来介绍rollup.js的不同使用方法,以及介绍一些比较常用的rollup插件。读者可以选择自己有兴趣的部分查看。

文章博客链接

本教程相关的所有demo都已上传到github,rollup-demos,欢迎star。

rollup.js简介

rollup.js

首先简单介绍一下rollup.JS。根据官方的介绍,rollup.js是一个模块打包工具,可以帮助你从一个入口文件开始,将所有使用到的模块文件都打包到一个最终的发布文件中(极其适合构建一个工具库,这也是我选择用rollup来打包的原因)。

rollup.js有两个重要的特性,其中一个就是它使用ES6的模块标准,这意味着你可以直接使用importexport而不需要引入babel(当然,在现在的项目中,babel可以说是必用的工具了)。

rollup.js的另一个重要特性叫做'tree-shaking',这个特性可以帮助你将无用代码(即没有使用到的代码)从最终的生成文件中删去。举个例子,我在A.js文件中定义了A1和A2两个方法,同时在B文件中引入了这两个方法,但是在B文件中只引入了A文件中的A1方法,那么在最后打包B文件时,rollup就不会将A2方法引入到最终文件中。(这个特性是基于ES6模块的静态分析的,也就是说,只有export而没有import的变量是不会被打包到最终代码中的)

rollup.js实例

demo0 开始使用rollup

初始化一个工程,创建一个依赖模块文件lib.js和入口文件index.js。

export function logA() {
    console.log('function logA called')
}

export function logB() {
    console.log('function logB called')
}
import { logA } from './lib'

logA()

现在我们要把lib.js和index.js打包成dist.js,首先要做的就是安装rollup.js。

在这里我们有两种安装方法:

  1. 全局安装:

打开你的命令行,输入npm install rollup -g,等待rollup安装完毕。安装完成之后,试着输入rollup -v来查看一下rollup是否安装成功了

查看rollup版本

成功安装完rollup之后,进入到工程目录下,输入打包命令rollup index.js -o dist.js,index.js 是我们的入口文件, -o 表示输出文件的路径,在 -o 后面跟着的 dist.js 就是我们要生成的最终打包文件了。(其实这里本来应该加上一个参数-i,用来表示入口文件的路径,但rollup是会把没有加参数的文件默认为是入口文件,因此我们在这里省略了这个参数)

使用全局rollup进行打包

显示出这条信息之后,我们发现目录下已经多出了一个 dist.js 文件,打开文件,我们发现里面的代码是这样的

function logA() {
    console.log('function logA called');
}

logA();

此时我们就已经完成了打包作业,可以将dist.js引入到HTML文件或是node模块中了

  1. 项目本地安装:

进入到项目目录,打开命令行输入npm install rollup --save-dev,把rollup加入到开发依赖中,然后在命令行中输入./node_modules/.bin/rollup index.js -o dist.js

使用项目本地rollup进行打包

或者在package.json文件中添加npm scripts命令"build": "rollup index.js -o dist.js",在命令行中输入npm run build来进行打包

使用项目本地rollup进行打包

在打包完成之后,我们查看一下效果,新建一个index.html文件,在这个文件中引入我们打包出来的dist.js文件

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>rollup 打包测试</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
    </head>
    <body>
        <script data-original="./dist.js"></script>
    </body>
</html>

用浏览器打开index.html文件,打开控制台,我们可以看到控制台上输出了一行文字

rollup打包文件测试

使用命令行运行dist.js文件,我们也可以看到命令行中输出了一行文字

rollup打包文件测试

这说明我们的打包文件dist.js是可以运行的,打包成功。

PS:

  1. 接下来的demo中,默认在项目内安装了rollup
  2. 接下来的demo中,非必要情况下不会对打包结果进行运行结果测试,读者若需要验证打包效果,请自己编写其他测试代码。

demo1 使用rollup进行模块化打包

在之前打包的过程中,命令行中输出了一行No format option was supplied – defaulting to 'es',这表示rollup并没有收到任何模块化的格式指令,因此会用默认的es模块标准来对文件进行打包。

如果在demo0中的index.js文件中把logA()改成export default logA(),那么rollup最后的打包结果就会是

function logA() {
    console.log('function logA called');
}

var index = logA();

export default index;

显然这样的代码是不能直接在浏览器端和node端运行的,我们需要把原先的ES6模块转化成浏览器和node支持的形式。

那么要去哪里找rollup把ES6代码转化成其他形式的方法呢?这里有两个方案,一是去rollup的官网找相关的资料,二是使用rollup命令行的帮助命令,看看能不能找到相关的参数

我们使用rollup命令行的帮助命令,在命令行中输入rollup -h

rollup命令行帮助

在这里我们可以看到类似版本号,帮助,使用配置文件等一系列参数。在这里我们可以找到-f这个参数,他的说明是输出的类型(amd,cjs,es,iife,umd),从括号里的内容我们可以看出,使用这个参数可以确定打包完后的文件的模块处理方式。(如果你还不知道这几种模块之间的区别,建议先去找一下相关的资料学习一下)

接下来我们用rollup来打包一下,在demo0中的index.js文件里将logA()改成export default logA(),在package.json文件中写好不同模块的打包命令

"build:amd": "rollup index.js -f amd -o ./dist/dist.amd.js",
"build:cjs": "rollup index.js -f cjs -o ./dist/dist.cjs.js",
"build:es": "rollup index.js -f es -o ./dist/dist.es.js",
"build:iife": "rollup index.js -f iife -n result -o ./dist/dist.iife.js",
"build:umd": "rollup index.js -f umd -n result -o ./dist/dist.umd.js",
"build:all": "npm run build:amd && npm run build:cjs && npm run build:es && npm run build:iife && npm run build:umd"

在这里我们发现在设置模块为iife(立即执行函数)和umd时,还加上了一个参数-n,这是因为我们将logA()的结果设置为模块的输出结果,那么在使用iife和umd时,需要事先设定模块的名称,才能让其他人通过这个模块名称引用到你的模块的输出结果。

在命令行中输入npm run build:all,运行所有打包命令,查看效果

demo1打包结果

可以看到已经输出了5种不同模块标准的打包文件,由于字数原因,在这里我们只查看一个打包文件(dist.iife.js)的内容

var result = (function () {
'use strict';

function logA() {
    console.log('function logA called');
}

var index = logA();

return index;

}());

可以看到所有代码都被打包到了一个立即执行函数中,并且将函数的返回值(模块的输出内容)赋值给了一个全局变量,而这个全局变量的名称就是我们之前设置的模块名称。

PS: 使用amd模块打包方式时,若不指定模块名称,则会打包成匿名函数,若想打包成一个具名函数,则需要使用-u--id来指定具名函数名称。

除了-f之外,还有许多其他的参数可以使用,看到这里可能有些同学会觉得麻烦了,这么多参数用起来好麻烦,每次都要输一长串的命令,那么有没有更好的方法来控制rollup的参数配置呢?

当然有,接下来我们就尝试使用配置文件来控制rollup打包。

demo2 使用配置文件来进行rollup打包

创建一个demo2,沿用之前demo1的内容,我们在demo2的项目下创建一个文件,取名为rollup.config.js,这个文件就是rollup的配置文件了,rollup根据配置文件的输出配置来进行打包,接下来我们在配置文件中输入配置代码:

export default {
  entry: 'index.js',
  format: 'cjs',
  dest: './dist/dist.js'
}

entry表示打包的入口文件,format表示要打包成的模块类型,dest表示输出文件的名称路径

PS: 若使用iife或umd模块打包,需要添加属性moduleName,用来表示模块的名称;若用amd模块打包,可以配置amd相关的参数(使用umd模块模式时,也会使用到amd相关配置参数):

amd: {
    id: 'amd-name',   // amd具名函数名称
    define: 'def'     // 用来代替define函数的函数名称
}

在这里我们发现配置文件也是使用了ES6语法,这是因为rollup可以自己处理配置文件,所以能够直接用ES6的模块输出(当然,你也可以选择使用node的module.exports方式来输出配置。

在package.json文件中编写npm scripts命令

"build": "rollup -c"

-c这个参数表示使用配置文件来进行打包,若后面没有指定使用的配置文件路径,则使用默认的配置文件名称rollup.config.js

在命令行中输入npm run build,执行打包,可以看到生成了打包文件dist.js

'use strict';

function logA() {
    console.log('function logA called');
}

var index = logA();

module.exports = index;

进阶: 当rollup配置文件最终输出的不是一个对象而是一个数组时,rollup会把每一个数组元素当成一个配置输出结果,因此可以在一个配置文件内设置多种输出配置

例如,我们添加一个indexB.js文件,在这个文件中我们将logA替换为logB,并将rollup配置文件改为:

export default [{
  entry: 'index.js',
  format: 'cjs',
  dest: './dist/distA.js'
},{
  entry: 'indexB.js',
  format: 'iife',
  moduleName: 'indexB',
  dest: './dist/distB.js'
}]

运行打包命令,发现在dist目录下生成了distA.js和distB.js两个文件,说明多项配置打包成功。

除了上面这种输出一个配置数组之外,你还可以通过配置target属性来输出多个打包文件:

export default {
  entry: 'index.js',
  targets: [{
      dest: 'dist/bundle.cjs.js',
      format: 'cjs'
    },
    {
      dest: 'dist/bundle.umd.js',
      moduleName: 'res',
      format: 'umd'
    },
    {
      dest: 'dist/bundle.es.js',
      format: 'es'
    },
  ]
}

这样配置会在dist目录下面输出bundle.cjs.jsbundle.umd.jsbundle.es.js三个打包文件,同时umd模块的名称会被定义成res。

demo3 监听文件变化,随时打包

我们在开发过程中,需要频繁对源文件进行修改,如果每次都自己手动输一遍打包命令,那真的是要烦死。因此,我们选择使用rollup提供的监听功能,安装rollup-wacth模块,再在rollup命令后面加上-w参数,就能让rollup监听文件变化,即时打包。

安装watch包:

npm i rollup-watch --save-dev
// or
yarn add rollup-watch --dev

编写npm scripts:

"dev": "rollup -c -w"

执行npm run dev,看到下面的提示:

rollup 监听文件变化

好了,这个时候你就可以随便修改你的源文件了,rollup会自动为你打包的。

PS: 若是你不想监听某些文件,只要在配置文件中加上

watch: {
    exclude: ['path/to/file/which/you/want/to/ignore']
}

就行了,其中的exclude表示你想要忽略的文件的路径(支持glob模式匹配)

demo4 是时候写ES6了

ES6可以说是现代JS开发100%会用到的技术了,rollup虽然支持了解析importexport两种语法,但是却不会将其他的ES6代码转化成ES5代码,因此我们在编写ES6代码的时候,需要引入插件来支持ES6代码的解析。

  1. 安装插件和你需要的babel preset:
npm i rollup-plugin-babel babel-preset-es2015 --save-dev
// or
yarn add rollup-plugin-babel babel-preset-es2015 --dev
  1. 创建.babalrc文件:
{
  "presets": [
    ["es2015", {
        "modules": false
    }]
  ]
}

之所以使用modules:false这个参数,是因为rollup默认是通过ES6模块语法来解析文件间的依赖,rollup默认是不支持解析common.js的模块规范的(怎么让rollup支持我会在接下来的demo中讲解),因此需要让babel不转化模块相关的语法,不然rollup在使用过程中会报错。

  1. 编写rollup配置文件:
import babel from 'rollup-plugin-babel';

export default [{
  entry: 'index.js',
  format: 'iife',
  dest: './dist/dist.js',
  plugins: [
    babel({
      exclude: 'node_modules/**'
    })
  ]
}]

rollup的配置文件的plugins属性可以让你添加在rollup打包过程中所要用到的插件,但是要注意的是,插件的添加顺序决定了它们在打包过程中的使用顺序,因此要注意配置文件的插件使用顺序。

  1. 编写ES6代码

在这里我们新建三个文件,两个类Person和Man和一个入口文件index.js

export default class Person {
    constructor (name, gender = '男') {
        this.name = name
        this.gender = gender
    }

    say () {
        console.log(`我的名字是${this.name},是一个${this.gender}生`)
    }
}
import Person from './Person'

export default class Man extends Person {
    constructor (name) {
        super(name, '男')
    }
}
import Man from './src/Man'

new Man('KainStar').say()
  1. 运行打包命令npm run build

rollup babel打包1

可以看到rollup输出了一段提示文字,我们先不去管它,先看看打包出来的文件能不能运行,执行node dist/dist.js

rollup babel打包2

可以看到代码运行成功了,那么我们回来继续看之前的提示文字,它的意思是'classCallCheck'这个babel helper函数使用了多次,rollup推荐我们使用external-helpers这个插件或es2015-rollup这个babel-preset来简化打包出来的代码。

我们查看一下打包出来的dist.js文件,发现_classCallCheck这个函数被定义了两次,分别被取名为_classCallCheck和_classCallCheck$1,这样的代码肯定是可以简化的,因此我们引入external-helpers这个插件:

npm i babel-plugin-external-helpers --save-dev
// or
yarn add babel-plugin-external-helpers --dev

修改.babelrc文件为

{
    "presets": [
        ["es2015", {
            "modules": false
        }]
    ],
    "plugins": [
        "external-helpers"
    ]
}

或者在配置文件中使用babel配置

plugins: [
    babel({
        plugins: ['external-helpers']
    })
]

注意! 在rollup-plugin-babel的官方github仓库中有一段配置是这样的:

plugins: [
    babel({
      plugins: ['external-helpers'],
      externalHelpers: true
    })
]

这段配置的使用要求是你需要设置全局的babelHelpers对象,以此来将打包文件中的babel相关代码删除,所以一般情况下不需要使用externalHelpers这个属性。

PS: 你也可以使用babel-preset-es2015-rollup这个包(搭配babel-core),它集成了babel-preset-es2015,babel-plugin-transform-es2015-modules-commonjs和babel-plugin-external-helpers三个模块,使用起来更加方便,只要将.babelrc文件修改成{ "presets": ["es2015-rollup"] }就可以使用了。

demo5 解析cjs,打包第三方模块

有时候我们会引入一些其他模块的文件(第三方的或是自己编写的),但是这些第三方的模块为了能够直接使用,往往不是ES6模块而是用commonjs的模块方式编写的,这个时候我们需要将commonjs的模块转化为ES6模块,这样才能让rollup进行正确的解析。

  1. 解析commonjs

解析commonjs需要引入一个rollup插件——rollup-plugin-commonjs

安装插件

npm i rollup-plugin-commonjs --save-dev
// or
yarn add rollup-plugin-commonjs --dev

在配置文件中配置插件

import commonjs from 'rollup-plugin-commonjs'

export default {
  entry: 'index_cjs.js',
  format: 'iife',
  dest: './js/dist_cjs.js',
  plugins: [
    commonjs()
  ]
}

编写cjs模块的文件

exports.logA = function logA() {
    console.log('function logA called')
}

exports.logB = function logB() {
    console.log('function logB called')
}

执行打包,可以看到打包成功,也没有输出任何提示信息

rollup cjs打包

  1. 打包第三方模块

在打包第三方模块的过程中,rollup无法直接解析npm模块,因此需要引入插件rollup-plugin-node-resolve并配合之前的commonjs插件来解析这些第三方模块

安装插件和第三方模块

npm i rollup-plugin-node-resolve lodash --save-dev
// or
yarn add rollup-plugin-node-resolve lodash --dev

在配置文件中配置插件

import commonjs from 'rollup-plugin-commonjs'
import resolve from 'rollup-plugin-node-resolve'

export default {
  entry: 'index_module.js',
  format: 'iife',
  dest: './js/dist_module.js',
  plugins: [
    resolve({
      jsnext: true,
      main: true,
      browser: true
    }),
    commonjs()
  ]
}

jsnext表示将原来的node模块转化成ES6模块,main和browser则决定了要将第三方模块内的哪些代码打包到最终文件中。

由于commonjsnode-resolve中的配置属性很多,因此不一一解释,希望了解更多的同学可以去官方仓库查看说明。

编写入口文件

import compact from 'lodash/compact'

const array = [0, 1, false, 2, '', 3]
const compctedArray = compact(array)
console.log(compctedArray)

在这里我们只引用了lodash中的compact方法,那么在最终代码里,应该也只会添加compact方法的代码。

执行打包命令,查看打包出来的文件:

(function () {
'use strict';

/**
 * Creates an array with all falsey values removed. The values `false`, `null`,
 * `0`, `""`, `undefined`, and `NaN` are falsey.
 *
 * @static
 * @memberOf _
 * @since 0.1.0
 * @category Array
 * @param {Array} array The array to compact.
 * @returns {Array} Returns the new array of filtered values.
 * @example
 *
 * _.compact([0, 1, false, 2, '', 3]);
 * // => [1, 2, 3]
 */
function compact(array) {
  var index = -1,
      length = array == null ? 0 : array.length,
      resIndex = 0,
      result = [];

  while (++index < length) {
    var value = array[index];
    if (value) {
      result[resIndex++] = value;
    }
  }
  return result;
}

var compact_1$1 = compact;

const array = [0, 1, false, 2, '', 3];
const compctedArray = compact_1$1(array);
console.log(compctedArray);

}());

确实只添加了compact方法的代码,而没有将lodash全部引入。

demo6 不要打包到一个文件,为rollup设置外部模块和全局变量

在平时的开发中,我们经常会引入其他的模块,但是在使用的时候,我们又不想把它们打包到一个文件里,想让他们作为单独的模块(或文件)来使用,方便浏览器端进行缓存,这个时候就需要使用配置文件中的external属性了

我们在demo5的基础上,把jquery安装到第三方模块中

npm i jquery --save-dev
// or
yarn add jquery --dev

将配置文件改成

import commonjs from 'rollup-plugin-commonjs'
import resolve from 'rollup-plugin-node-resolve'

export default {
  entry: 'index.js',
  format: 'iife',
  dest: './js/dist.js',
  external: ['jquery'],
  plugins: [
    resolve({
      jsnext: true,
      main: true,
      browser: true
    }),
    commonjs()
  ]
}

external用来表示一个模块是否要被当成外部模块使用,属性的值可以是一个字符串数组或一个方法,当传入的是一个字符串数组时,所有数组内的模块名称都会被当成是外部模块,不会被打包到最终文件中

当传入的是一个方法时,方法有一个参数id,表示解析的模块的名称,我们可以自定义解析方式,若是要当做外部模块不打包到最终文件中,则返回true,若要一起打包到最终文件中,则返回false

在这里我们把jquery当成一个外部模块,执行打包命令:

rollup 添加外部模块

检查打包出来的文件,我们发现lodash的compact方法依旧被打包进了最终文件中,但是jquery却没有被打包进去,而是以$的全局变量形式被传入到了立即执行函数中。

在这里rollup又给我们输出了一条提示信息,意思是我们没有在配置文件中给外部模块jquery设置全局变量名称,因此rollup自己猜测了一个名称$,当成是依赖的全局变量名。

如果直接使用全局的$的话,可能会因为变量$被其他引入的代码覆盖而报错,因此我们要将$替换为不容易冲突的jQuery变量,在配置文件中添加globals属性:

globals: {
    jquery: 'jQuery'
}

globals的值是一个对象,key表示使用的模块名称(npm模块名),value表示在打包文件中引用的全局变量名,在这里我们就是把jquery模块的全局变量名设置为jQuery,重新打包

在重新打包出来的文件中,我们发现最后传入的参数已经由$变为了jQuery,而且rollup也没有输出提示信息。

demo7 打包node内置模块

有时候我们想要在浏览器端使用node自带的一些内置模块,一般情况下会使用browserify这个工具来打包,但是browserify打包出来的文件实在太大,因此我们用rollup选择性地导入我们需要的node内置模块

安装插件

npm i rollup-plugin-node-builtins --save-dev
// or
yarn add rollup-plugin-node-builtins --dev

PS: node-builtins对不同的node内置模块支持不同,有些模块可能需要使用其他的插件(例如rollup-plugin-node-globals)才能正常打包,具体的支持情况可以查看node-builtins的官方仓库

编写配置文件

import builtins from 'rollup-plugin-node-builtins'

export default {
  entry: 'index.js',
  format: 'iife',
  dest: './dist/dist.js',
  plugins: [
    builtins()
  ]
}

编写入口文件

import { join } from 'path'

const path_base = 'E://node'
const path_joined = join(path_basem, 'bin')
console.log(path_joined)

在这里我们使用node内置的path模块,运行打包命令,发现dist.js文件中引入了额外的100多行代码,这100多行代码就实现了path模块的join方法供我们使用。

PS: 我建议,如果不是必要的情况,最好能够使用其他人编写的第三方实现库或自己造轮子实现,而不是使用node内置的模块,因为在引用某些模块时,node-builtins可能会引入过多的代码,这样会大大增加最后打包的文件的大小,使用他人的第三方库或自己的实现可控性更高

demo8 配合CDN来使用rollup

有时候我们可能会使用CDN服务器上的js文件,但是又不想在本地安装一个相同的模块(也有可能没有对应的模块),可能在版本升级的时候会产生一些问题,这个时候我们就需要使用rollup的paths属性了,这个属性可以帮助你把依赖的代码文件地址注入到打包之后的文件里。

编写配置文件

export default {
  entry: 'index.js',
  format: 'amd',
  dest: './dist/dist.js',
  external: ['jquery'],
  paths: {
    jquery: 'https://cdn.bootcss.com/jquery/3.2.1/jquery.js'
  }
}

在这里我们要使用cdn上的jquery文件,paths属性的值可以是一个对象或用法与external属性方法相似的方法(只是返回的不是boolean值而是文件的地址)。若使用对象来表示,则key值为需要引入的模块名称,value值为对应的文件地址

编写源文件

import $ from 'jquery'

$('#p').html('rollup 使用paths属性配合CDN')

执行打包命令,最后打包出来的文件内容是:

define(['https://cdn.bootcss.com/jquery/3.2.1/jquery.js'], function ($) { 'use strict';

$ = $ && $.hasOwnProperty('default') ? $['default'] : $;

$('#p').html('rollup 使用paths属性配合CDN');

});

可以看到rollup已经把我们需要的CDN地址作为依赖加入到了打包文件中。

demo9 最小化你的代码

代码发布时,我们经常会把自己的代码压缩到最小,以减少网络请求中的传输文件大小。

rollup的插件rollup-plugin-uglify就是来帮助你压缩代码的,我们接下来就用这个插件来压缩一下我们的代码

npm i rollup-plugin-uglify --save-dev
// or
yarn add rollup-plugin-uglify --dev

编写配置文件

import uglify from 'rollup-plugin-uglify'

export default {
  entry: 'index.js',
  format: 'iife',
  dest: './dist/dist.js',
  plugins: [
    uglify()
  ]
}

运行打包命令,查看dist.js文件,发现代码已经被压缩了

但是,压缩过的代码在debug时会带来很大的不便,因此我们需要在压缩代码的同时生成一个sourceMap文件

幸运的是,rollup自己就支持sourceMap文件的生成,不需要我们去引入其他插件,只需要在配置文件中加上:

sourceMap: true

就可以了。

重新打包,我们发现不仅生成了dist.js.map文件,而且dist文件最后加上了一行//# sourceMappingURL=dist.js.map,并且在浏览器中可以正确加载源文件

rollup sourceMap

PS: 若是将sourceMap属性的值设置为inline,则会将sourceMap的内容添加到打包文件的最后。

demo10 为你的代码添eslint检查

在大型工程的团队开发中,我们需要保证团队代码风格的一致性,因此需要引入eslint,而且在打包时需要检测源文件是否符合eslint设置的规范,若是不符合则抛出异常并停止打包。在这里我们使用rollup的eslint插件rollup-plugin-eslint:

安装插件

npm i eslint rollup-plugin-eslint --save-dev
// or
yarn add eslint rollup-plugin-eslint --dev

编写eslint配置文件.eslintrc

{
    "env": {
        "browser": true,
        "commonjs": true,
        "es6": true,
        "node": true
    },
    "parserOptions": {
        "ecmaFeatures": {
            "jsx": false
        },
        "sourceType": "module"
    },
    "rules": {
        "semi": ["error","never"]
    }
}

在这里我们强制要求不使用分号,然后在源文件中加上一个分号

foo(element);

编写rollup配置文件

import eslint from 'rollup-plugin-eslint';

export default {
  entry: './src/index.js',
  format: 'iife',
  dest: './dist/dist.js',
  plugins: [
    eslint({
      throwOnError: true,
      throwOnWarning: true,
      include: ['src/**'],
      exclude: ['node_modules/**']
    })
  ]
}

eslint插件有两个属性需要说明:throwOnError和throwOnWarning设置为true时,如果在eslint的检查过程中发现了error或warning,就会抛出异常,阻止打包继续执行(如果设置为false,就只会输出eslint检测结果,而不会停止打包)

执行打包命令,发现eslint在输出了检查结果之后抛出了异常,而且dist.js文件也没有生成

rollup eslint抛出异常

删除index.js文件中的分号,重新打包,发现打包成功

进阶: 在平时的开发过程中,我们经常会使用IDE或编辑器的eslint插件,以便提早发现问题,但是有时候这些插件会去检查打包完的文件,导致你的提示框里一直会有eslint检测到错误的消息

我们现在有两种解决方案,一是创建一个.eslintignore文件,将打包文件加进去,让eslint忽略这个文件

还有一种就是让rollup在打包文件的开始和最后自动生成注释来阻止eslint检测代码,使用这种方法时,需要使用rollup配置文件的两个属性:banner和footer,这两个属性会在生成文件的开头和结尾插入一段你自定义的字符串。我们利用这个属性,在打包文件的开头添加/*eslint-disable */注释,让eslint不检测这个文件。

添加banner和footer属性

banner: '/*eslint-disable */'

重新打包,我们发现打包文件的开头被插入了这段注释字符串,而且eslint插件也不报dist.js文件的错了

/*eslint-disable */
(function () {
'use strict';

// 具体代码

}());

demo11 控制开发环境和生产环境下的配置

  1. 配置文件的开发/生产环境配置

有时候我们会需要区分开发环境和生产环境,针对不同的打包要求输出不同的打包配置,但是我们又不想写rollup.config.dev.jsrollup.config.prod.js两个文件,因为可能两者之间的区别只是一个uglify插件。

因此,我们就需要用变量来控制配置文件的输出内容,rollup命令行给我们提供了一个设置环境变量的参数--environment,在这个参数后面加上你需要设置的环境变量,不同变量间用逗号分隔,用冒号后面的字符串表示对应变量的值(若不加冒号,则默认将值设为字符串true):

在package.json文件中编写对应的npm scripts命令:

"dev": "rollup -c --environment NODE_ENV:development",
"build": "rollup -c --environment NODE_ENV:production"

最后修改我们的rollup配置文件

import uglify from 'rollup-plugin-uglify'

let isProd = process.env.NODE_ENV === 'production'

// 通用的插件
const basePlugins = []
// 开发环境需要使用的插件
const devPlugins = []
// 生产环境需要使用的插件
const prodPlugins = [uglify()]

let plugins = [...basePlugins].concat(isProd ? prodPlugins:devPlugins)
let destFilePath = isProd ? './dist/dist.min.js': './dist/dist.js'

export default {
  entry: 'index.js',
  format: 'iife',
  dest: destFilePath,
  sourceMap: isProd,
  plugins: plugins
}

我们分别运行两个npm scripts命令,查看打包的结果:

rollup 开发环境和生产环境打包结果

  1. 源文件开发/生产环境信息注入

上面是在配置文件里通过变量来改变输出的配置类型,但是我们有时候需要将生产环境信息添加到源文件里,这个时候就需要使用rollup的配置属性intro和outro了

如果说banner和footer是在文件开始和结尾添加字符串,那么intro和outro就是在被打包的代码开头和结尾添加字符串了,以iife模式来举例,如果我们配置了这四个属性,那么输出结果就会是:

// banner字符串
(function () {
'use strict';
// intro字符串

// 被打包的代码

// outro字符串
}());
// footer字符串

这样的形式

下面我们实际使用一下,在index.js文件里加上一段需要依赖的代码

if (DEVELOPMENT) {
    console.log('处于开发环境')
} else {
    console.log('处于生产环境')
}

然后在我们的rollup配置文件里添加:

intro: 'var DEVELOPMENT = ' + !isProd,

这样,当我们最后生成的代码时,就会输出开发环境或生产环境的提示:

rollup 开发环境和生产环境信息打包结果

  1. 源文件开发/生产环境信息替换

有时候我们会把开发/生产环境的信息直接写在源文件里面,这个时候用intro来注入代码的方式就不适合了。这个时候我们就需要使用rollup-plugin-replace插件来对源代码的变量值进行替换:

安装插件

npm i rollup-plugin-replace --save-dev
// or
yarn add rollup-plugin-replace --dev

编写配置文件

const basePlugins = [replace({
  DEVELOPMENT: !isProd
})]

// 将intro属性注释掉
// intro: 'var DEVELOPMENT = ' + !isProd,

这里我们使用replace插件,以key-value对象的形式,将DEVELOPMENT的值替换为!isProd的值

执行打包命令,并检查打包结果:

rollup 开发环境和生产环境信息打包结果

进阶: replace除了直接使用key-value的形式替换对应key同名变量的方法之外,还可以通过配置delimiters参数来实现模板功能:

配置replace插件参数

VERSION: '1.0.0',
delimiters: ['{{', '}}']

通过这个配置,在打包过程中,{{VERSION}}会被替换成1.0.0

在index.js文件内添加相关代码

var version = '{{VERSION}}'
console.log('版本 v' + version)

打包的结果

var version = '1.0.0';
console.log('版本 v' + version);

demo12 使用rollup的API

有时候我们会需要在打包的前后执行一些其他的代码,但是又不想引入其他构建工具(例如gulp),那么就可以使用rollup提供的node API来编写你自己的打包流程。

rollup模块只提供了一个rollup函数,这个函数的参数和我们编写配置文件时导出的参数不同,减少了很多配置属性,留下来的主要是一些输入相关的配置。(具体的配置属性可以查看rollup wiki的javascript API一节)

执行这个函数返回的是一个Promise,并且在then方法中提供一个bundle对象作为参数,这个对象保存了rollup对源文件编译一次之后的结果,而且提供了generatewrite两个方法

write方法提供了编译并将打包结果输出到文件里的功能,返回的是一个没有参数的Promise,可以让你自定义接下来执行的代码

generate方法是只提供了编译的功能,返回一个Promise,这个Promise有一个对象参数,包含了code(编译完之后的代码)和map(分析出来的sourceMap对象)两个属性,一般用在插件开发中

write和gengerate方法都接受有编译相关属性的对象作为传入的编译参数,而write方法还额外接受dset属性作为导出文件的名称。

在这里我们只使用write方法来编写一个为所有模块类型打包,并输出打包完毕提示的文件,至于generate的使用方法我们会放在编写插件一节中介绍。

const rollup = require('rollup').rollup

rollup({
    entry: 'index.js'
}).then(bundle => {

    // 保存所有Promise的列表
    let writePromiseList = []
    // 声明所有需要打包的模块类型
    let moduleTypesList = ['es','cjs','amd','umd','iife']

    moduleTypesList.forEach(function(moduleType) {
        writePromiseList.push(bundle.write({
            dest: './dist/dist.' + moduleType + '.js',
            format: moduleType,
            sourceMap: true
        }))
    })

    return Promise.all(writePromiseList)

}).then(() => {
    console.log('全部模块格式打包完毕')
    // 其他代码
})

将package.json文件内的npm scripts命令修改为

"build": "node rollup.js"

执行打包命令,查看打包结果

rollup 自定义打包结果1

rollup 自定义打包结果2

在这里我们可以看到,一个bundle可以被重复使用多次,因此我们可以用Promise.all方法来等待所有模块打包完成后再输出打包完毕的提示。

demo13 除了打包JS,我们还能……

一个web项目内肯定不会只有js文件,还有css、html(也可能是模板文件)和其他类型的文件,那么我们在打包的时候能不能把这些文件一起打包呢?

我们需要区分一下,在这里的打包有两种意思,一种是让这些文件可以像JS文件一样,在源代码中被import并使用;还有一种是通过在源文件中import这些文件,最后将它们合并到一起并导出到一个最终文件内。

不同的rollup插件有不同的效果,在使用的时候一定要查看插件的相关说明

安装插件

npm i rollup-plugin-scss --save-dev
// or
yarn add rollup-plugin-scss --dev

编写配置文件

import scss from 'rollup-plugin-scss'

export default {
  entry: './src/js/index.js',
  format: 'iife',
  dest: './dist/js/dist.js',
  sourceMap: true,
  plugins: [
    scss({
      output: './dist/css/style.css'
    })
  ]
}

在这里我们尝试编译和打包scss文件,将其合并成一个style.css文件,并输出到dist/css目录下

编写scss文件

$blue: #69c4eb;

.bg-blue {
    background-color: $blue
}
$white: #fff;

.text-white {
    color: $white;
}

然后在源文件中引用这两个scss文件

import '../scss/text.scss'
import '../scss/bg.scss'

var html = `
    <div class="bg-blue">
        <p class="text-white">测试文字</p>
    </div>
`

document.body.innerHTML = html

执行打包命令,查看效果

rollup 打包scss效果

extra 编写你自己的rollup插件

有时候我们可能需要自己编写rollup插件来实现需求,rollup官方在wiki上提供了关于编写插件的一些介绍,下面我们就根据这些介绍来写一个自己的rollup插件。

我们在这里仿照scss插件编写一个stylus的rollup插件,让使用者可以import stylus文件,并编译打包导出到指定的目录下(为了节省代码量,只写了输出到指定路径的功能代码,其他的功能可以参考scss插件的具体代码)。

首先创建项目,在package.json文件中,除了一般信息之外,还要加上

"main": "index.cjs.js",
"module": "index.es.js",
"jsnext:main": "index.es.js"

这些信息用来区分使用不同模块规范时使用的文件

安装我们需要用到的模块

npm i rollup rollup-plugin-babel babel-preset-es2015-rollup babel-core --save-dev
npm i rollup-pluginutils stylus --save
// or
yarn add rollup rollup-plugin-babel babel-preset-es2015-rollup babel-core --dev
yarn add rollup-pluginutils stylus

rollup-pluginutils和stylus是我们运行时需要的两个模块,stylus用来解析stylus文件,pluginutils则提供给了我们一些编写插件常用的函数

编写rollup配置文件

import babel from 'rollup-plugin-babel'

export default {
    entry: './index.es.js',
    dest: './index.cjs.js',
    format: 'cjs',
    plugins: [
        babel()
    ]
}

rollup插件需要一个含有指定属性的对象作为插件内容,rollup官方建议我们在编写插件的时候,export一个返回值为插件对象的函数,这样可以方便使用者指定插件的参数。

rollup会将解析的部分结果作为参数调用插件返回的对象中的一些函数属性,这些函数会在合适的时候被rollup调用(相当于rollup在执行各个操作时的钩子函数),下面我们介绍一些常用的属性:

  • name:插件的名称,提供给rollup进行相关信息的输出
  • load:不指定这个属性时,解析模块会默认去读取对应路径文件的内容;而当该值为函数(id => code)时,可以将函数最后的返回值作为文件的内容提供给rollup(可以用来生成自定义格式的代码)
  • resolveId:一个( (importee, importer) => id)形式的函数,用来解析ES6的import语句,最后需要返回一个模块的id
  • transform:最常使用的属性,是一个函数,当rollup解析一个import时,会获取到对应路径文件的内容,并将内容和模块的名称作为参数提供给我们;这个函数执行完毕之后,需要返回一个作为代码的字符串或是类似{ code, map }结构的对象,用来表示解析完之后该模块的实际内容,map指的是sourceMap,而如果我们没有要导出的sourceMap,就可以将返回的map值设为{mappings: ''}
  • ongenerate:当我们或rollup调用generate方法时,会被调用的一个钩子函数,接受generate的option作为参数
  • onwrite:和ongenerate一样,调用write方法时,会被调用的一个钩子函数,接受write的option作为参数

一般情况下,我们通过transform函数来获取文件的id和内容,并对内容做一些处理,若需要输出文件则使用ongenerate或onwrite在rollup打包的最后阶段来做相应的输出。

load和resolveId在一般情况下不会使用,除非你有特殊的需求(例如对路径、模块id进行修改等)

根据上面这些内容,我们编写具体的插件内容

import { createFilter } from 'rollup-pluginutils'
import fs from 'fs'
import path from 'path'
import stylus from 'stylus'

// 递归创建文件夹
function mkdirs(dir) {
    return new Promise((resolve, reject) => {
        fs.exists(dir, (exist) => {
            if (exist) {
                resolve()
            } else {
                mkdirs(path.dirname(dir)).then(() => {
                    fs.mkdir(dir, (err) => {
                        if (err) {
                            reject()
                        } else {
                            resolve()
                        }
                    })
                })
            }
        })
    })
}

// 导出一个function
export default function stylusPlugin(options = {}) {
    // 创建一个文件过滤器,过滤以css,styl结尾的文件
    const stylusFilter = createFilter(options.include || ['**/*.css', '**/*.styl'], options.exclude)

    // dest用来保存指定的输出路径
    let dest = options.output,
    // styleNodes用来暂存不同文件的css代码
        styleNodes = {}

    // 编译stylus文件
    function complier(str, stylusOpt) {
        return new Promise((resolve, reject) => {
            stylus.render(str, stylusOpt, (err, css) => {
                if (err) {
                    reject(err)
                } else {
                    resolve(css)
                }
            })
        })
    }

    return {
        // 插件名称
        name: 'rollup-plugin-stylus',

        // 解析import时调用,获取文件名称和具体代码,将它们保存起来
        transform (code, id) {
            if (!stylusFilter(id)) {
                return
            }

            styleNodes[id] = code
            return ''
        },
        // generate时调用,用stylus解析代码,并输出到指定目录中
        async ongenerate (genOpt) {
            let css = ''
            for (let id in styleNodes) {
                // 合并所有css代码
                css += styleNodes[id] || ''
            }

            // 编译stylus代码
            if (css.length) {
                try {
                    css = await complier(css, Object.assign({}, options.stylusOpt))
                } catch (error) {
                    console.log(error)
                }
            }

            // 没有指定输出文件路径时,设置一个默认文件
            if (typeof dest !== 'string') {
                if (!css.length) {
                    return
                }

                dest = genOpt.dest || 'bundle.js'
                if (dest.endsWith('.js')) {
                    dest = dest.slice(0, -3)
                }
                dest = dest + '.css'
            }

            // 创建目录,并将css写入到结果文件内
            await mkdirs(path.dirname(dest))
            return new Promise((resolve, reject) => {
                fs.writeFile(dest, css, (err) => {
                    if (err) {
                        reject(err)
                    } else {
                        resolve()
                    }
                })
            })
        }
    }
}

这样,一个解析并打包stylus文件的rollup插件就写好了,你可以在你的工程中引用这个文件,也可以将其作为一个模块发布,以便于分享给其他人使用。

总结 and 一个完整的rollup项目的模板

rollup在打包JS上是一个十分快捷方便的工具,但和webpack相比,他的生态圈还是不够强大,对于大型web工程的适应度相对不足

rollup的优点在于方便的配置,天然的ES6模块支持让我们可以直接使用import和export语法,在打包JS上,不实现自己的模块机制,而是使用目前常见的模块规范有助于其他工具(例如requirejs)来引用打包文件;tree-shaking的特性也有助于减少代码量,因此我认为rollup比起构建应用工程项目,更适合用来构建一个JS库或node模块

我将上面介绍的插件集合到一起,添加了测试的支持,制作了一个较为完整的rollup工程模板。放在rollup-project-template目录下,需要的同学可以自取(你也可以增加或删除任意你需要的模块,来组建属于你自己的rollup项目模板)

参考资料

查看原文

xiayefeng 收藏了文章 · 2019-12-08

懒加载的3种实现方式

优势

  1. 性能收益:浏览器加载图片、decode、渲染都需要耗费资源,懒加载能节约性能消耗,缩短onload事件时间。
  2. 节约带宽:这个不需要解释。

通常,我们在html中展示图片,会有两种方式:

  1. img 标签
  2. css background-image

img的懒加载实现

img有两种方式实现懒加载:

  1. 事件监听(scroll、resize、orientationChange)
<!DOCTYPE html>
<html lang="en">
<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>event</title>
    <style>
        img {
            background: #F1F1FA;
            width: 400px;
            height: 300px;
            display: block;
            margin: 10px auto;
            border: 0;
        }
    </style>
</head>
<body>
    <img data-original="https://ik.imagekit.io/demo/img/image1.jpeg?tr=w-400,h-300" />
    <img data-original="https://ik.imagekit.io/demo/img/image2.jpeg?tr=w-400,h-300" />
    <img data-original="https://ik.imagekit.io/demo/img/image3.jpg?tr=w-400,h-300" />
    <img class="lazy" data-data-original="https://ik.imagekit.io/demo/img/image2.jpeg?tr=w-400,h-300" />
    <img class="lazy" data-data-original="https://ik.imagekit.io/demo/img/image3.jpg?tr=w-400,h-300" /> -->
    <img class="lazy" data-data-original="https://ik.imagekit.io/demo/img/image4.jpeg?tr=w-400,h-300" />
    <img class="lazy" data-data-original="https://ik.imagekit.io/demo/img/image5.jpeg?tr=w-400,h-300" />
    <img class="lazy" data-data-original="https://ik.imagekit.io/demo/img/image6.jpeg?tr=w-400,h-300" />
    <img class="lazy" data-data-original="https://ik.imagekit.io/demo/img/image7.jpeg?tr=w-400,h-300" />
    <img class="lazy" data-data-original="https://ik.imagekit.io/demo/img/image8.jpeg?tr=w-400,h-300" />
    <img class="lazy" data-data-original="https://ik.imagekit.io/demo/img/image9.jpeg?tr=w-400,h-300" />
    <img class="lazy" data-data-original="https://ik.imagekit.io/demo/img/image10.jpeg?tr=w-400,h-300" />
    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var lazyloadImages = document.querySelectorAll("img.lazy");    
            var lazyloadThrottleTimeout;
            
            function lazyload () {
                if(lazyloadThrottleTimeout) {
                    clearTimeout(lazyloadThrottleTimeout);
                }    
                
                lazyloadThrottleTimeout = setTimeout(function() {
                    var scrollTop = window.pageYOffset;
                    lazyloadImages.forEach(function(img) {
                        if(img.offsetTop < (window.innerHeight + scrollTop)) {
                            img.src = img.dataset.src;
                            img.classList.remove('lazy');
                        }
                    });
                    if(lazyloadImages.length == 0) {
                        document.removeEventListener("scroll", lazyload);
                        window.removeEventListener("resize", lazyload);
                        window.removeEventListener("orientationChange", lazyload);
                    }
                }, 20);
            }
            
            document.addEventListener("scroll", lazyload);
            window.addEventListener("resize", lazyload);
            window.addEventListener("orientationChange", lazyload);
        });
    </script>
</body>
</html>
  1. Intersection Observer(兼容性问题)
<!DOCTYPE html>
<html lang="en">
<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>observer</title>
    <style>
        img {
            background: #F1F1FA;
            width: 400px;
            height: 300px;
            display: block;
            margin: 10px auto;
            border: 0;
        }
    </style>
</head>
<body>
    <img data-original="https://ik.imagekit.io/demo/img/image2.jpeg?tr=w-400,h-300" />
    <img data-original="https://ik.imagekit.io/demo/img/image3.jpg?tr=w-400,h-300" /> -->
    <img class="lazy" data-data-original="https://ik.imagekit.io/demo/img/image1.jpeg?tr=w-400,h-300" />
    <img class="lazy" data-data-original="https://ik.imagekit.io/demo/img/image2.jpeg?tr=w-400,h-300" />
    <img class="lazy" data-data-original="https://ik.imagekit.io/demo/img/image3.jpg?tr=w-400,h-300" />
    <img class="lazy" data-data-original="https://ik.imagekit.io/demo/img/image4.jpeg?tr=w-400,h-300" />
    <img class="lazy" data-data-original="https://ik.imagekit.io/demo/img/image5.jpeg?tr=w-400,h-300" />
    <img class="lazy" data-data-original="https://ik.imagekit.io/demo/img/image6.jpeg?tr=w-400,h-300" />
    <img class="lazy" data-data-original="https://ik.imagekit.io/demo/img/image7.jpeg?tr=w-400,h-300" />
    <img class="lazy" data-data-original="https://ik.imagekit.io/demo/img/image8.jpeg?tr=w-400,h-300" />
    <img class="lazy" data-data-original="https://ik.imagekit.io/demo/img/image9.jpeg?tr=w-400,h-300" />
    <img class="lazy" data-data-original="https://ik.imagekit.io/demo/img/image10.jpeg?tr=w-400,h-300" />
    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var lazyloadImages = document.querySelectorAll(".lazy");
            var imageObserver = new IntersectionObserver(function(entries, observer) {
                entries.forEach(function(entry) {
                    if (entry.isIntersecting) {
                        var image = entry.target;
                        image.src = image.dataset.src;
                        image.classList.remove("lazy");
                        imageObserver.unobserve(image);
                    }
                });
            });
            lazyloadImages.forEach(function(image) {
                imageObserver.observe(image);
            });
        });
    </script>
</body>
</html>

background-image的实现

background-image的实现跟img的原理基本是一样的,区别是在对class的处理上:

<!DOCTYPE html>
<html lang="en">
<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>background</title>
    <style>
        body {
            margin: 0;
        }
        .bg {
            height: 200px;
        }
        #bg-image.lazy {
            background-image: none;
            background-color: #F1F1FA;
        }
        #bg-image {
            background-image: url("https://ik.imagekit.io/demo/img/image1.jpeg?tr=w-400,h-300");
            background-size: 100%;
        }
    </style>
</head>
<body>
    <div id="bg-image" class="bg lazy"></div>
    <div id="bg-image" class="bg lazy"></div>
    <div id="bg-image" class="bg lazy"></div>
    <div id="bg-image" class="bg lazy"></div>
    <div id="bg-image" class="bg lazy"></div>
    <div id="bg-image" class="bg lazy"></div>
    <div id="bg-image" class="bg lazy"></div>
    <div id="bg-image" class="bg lazy"></div>
    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var lazyloadImages = document.querySelectorAll(".lazy");
            var imageObserver = new IntersectionObserver(function(entries, observer) {
                entries.forEach(function(entry) {
                    if (entry.isIntersecting) {
                        var image = entry.target;
                        image.classList.remove("lazy");
                        imageObserver.unobserve(image);
                    }
                });
            });
            lazyloadImages.forEach(function(image) {
                imageObserver.observe(image);
            });
        });
    </script>
</body>
</html>

渐进式懒加载

渐进式懒加载,指的是存在降级处理,通常html形式如下:

<a href="full.jpg" class="progressive replace">
  <img data-original="tiny.jpg" class="preview" alt="image" />
</a>

这样的代码会有2个好处:

  1. 如果js执行失败,可以点击预览
  2. 大小与实际图一致的占位data URI,避免reflow

最终的代码如下:

<!DOCTYPE html>
<html lang="en">
<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>progressive</title>
    <style>
        a.progressive {
            position: relative;
            display: block;
            overflow: hidden;
            outline: none;
        }
        a.progressive:not(.replace) {
            cursor: default;
        }
        a.progressive img {
            display: block;
            width: 100%;
            max-width: none;
            height: auto;
            border: 0 none;
        }
        a.progressive img.preview {
            filter: blur(2vw);
            transform: scale(1.05);
        }
        a.progressive img.reveal {
            position: absolute;
            left: 0;
            top: 0;
            will-change: transform, opacity;
            animation: reveal 1s ease-out;
        }
        @keyframes reveal {
            0% {transform: scale(1.05); opacity: 0;}
            100% {transform: scale(1); opacity: 1;}
        }
    </style>
</head>
<body>
    <a href="https://s3-us-west-2.amazonaws.com/s.cdpn.io/123941/nature5.jpg" data-srcset="https://s3-us-west-2.amazonaws.com/s.cdpn.io/123941/nature5.jpg 800w, https://s3-us-west-2.amazonaws.com/s.cdpn.io/123941/nature5big.jpg 1600w" class="progressive replace">
        <img data-original="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD//gA7Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcgSlBFRyB2NjIpLCBxdWFsaXR5ID0gNzAK/9sAQwAKBwcIBwYKCAgICwoKCw4YEA4NDQ4dFRYRGCMfJSQiHyIhJis3LyYpNCkhIjBBMTQ5Oz4+PiUuRElDPEg3PT47/9sAQwEKCwsODQ4cEBAcOygiKDs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7/8AAEQgABQAUAwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/aAAwDAQACEQMRAD8A5yC3e2S3gM7OEQHdgA57VTuLAPqUoaUk7yM7R2BPSiivS5VN8stjmk2ldGVM3lyshG7bxk0UUV4skk2jdN2P/9k=" class="preview" alt="palm trees" />
    </a>
    <a href="https://s3-us-west-2.amazonaws.com/s.cdpn.io/123941/nature2.jpg" class="progressive replace">
        <img data-original="http://lorempixel.com/20/15/nature/2/" class="preview" alt="sunset" />
    </a>
    <a href="https://s3-us-west-2.amazonaws.com/s.cdpn.io/123941/nature3.jpg" class="progressive replace">
        <img data-original="http://lorempixel.com/20/15/nature/3/" class="preview" alt="tide" />
    </a>
    <a href="https://s3-us-west-2.amazonaws.com/s.cdpn.io/123941/nature5.jpg" data-srcset="https://s3-us-west-2.amazonaws.com/s.cdpn.io/123941/nature5.jpg 800w, https://s3-us-west-2.amazonaws.com/s.cdpn.io/123941/nature5big.jpg 1600w" class="progressive replace">
        <img data-original="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD//gA7Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcgSlBFRyB2NjIpLCBxdWFsaXR5ID0gNzAK/9sAQwAKBwcIBwYKCAgICwoKCw4YEA4NDQ4dFRYRGCMfJSQiHyIhJis3LyYpNCkhIjBBMTQ5Oz4+PiUuRElDPEg3PT47/9sAQwEKCwsODQ4cEBAcOygiKDs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7/8AAEQgABQAUAwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/aAAwDAQACEQMRAD8A5yC3e2S3gM7OEQHdgA57VTuLAPqUoaUk7yM7R2BPSiivS5VN8stjmk2ldGVM3lyshG7bxk0UUV4skk2jdN2P/9k=" class="preview" alt="palm trees" />
    </a>
    <a href="https://s3-us-west-2.amazonaws.com/s.cdpn.io/123941/nature2.jpg" class="progressive replace">
        <img data-original="http://lorempixel.com/20/15/nature/2/" class="preview" alt="sunset" />
    </a>
    <a href="https://s3-us-west-2.amazonaws.com/s.cdpn.io/123941/nature3.jpg" class="progressive replace">
        <img data-original="http://lorempixel.com/20/15/nature/3/" class="preview" alt="tide" />
    </a>
    <script>
        window.addEventListener('load', function() {
            var pItem = document.getElementsByClassName('progressive replace'), timer;

            window.addEventListener('scroll', scroller, false);
            window.addEventListener('resize', scroller, false);
            inView();

            function scroller(e) {
                timer = timer || setTimeout(function() {
                    timer = null;
                    requestAnimationFrame(inView);
                }, 300);
            }

            function inView() {
                var scrollTop = window.pageYOffset;
                var innerHeight = window.innerHeight;
                var p = 0;
                while (p < pItem.length) {
                    var offsetTop = pItem[p].offsetTop;
                    if (offsetTop < (scrollTop + innerHeight)) {
                        loadFullImage(pItem[p]);
                        pItem[p].classList.remove('replace');
                    }
                    else p++;
                }
            }


            function loadFullImage(item) {
                var img = new Image();
                if (item.dataset) {
                    img.srcset = item.dataset.srcset || '';
                    img.sizes = item.dataset.sizes || '';
                }
                img.src = item.href;
                img.className = 'reveal';
                if (img.complete) addImg();
                else img.onload = addImg;

                function addImg() {
                    item.addEventListener('click', function(e) { e.preventDefault(); }, false);
                    item.appendChild(img).addEventListener('animationend', function(e) {
                        var pImg = item.querySelector('img.preview');
                        if (pImg) {
                            e.target.alt = pImg.alt || '';
                            item.removeChild(pImg);
                            e.target.classList.remove('reveal');
                        }
                    });
                }

            }

        }, false);
    </script>
</body>
</html>

现成库

推荐下面这个库,使用非常简单:https://www.npmjs.com/package/lozad

查看原文

xiayefeng 关注了专栏 · 2019-12-08

有赞美业前端团队

关注 2785

xiayefeng 回答了问题 · 2019-09-04

禁止浏览器自动填充到表单

加个 value=""就可以了,

关注 20 回答 11

xiayefeng 回答了问题 · 2019-06-30

解决Jest 在 es2015 模块时出错

请问路主是怎么解决的

关注 2 回答 2

xiayefeng 收藏了文章 · 2019-01-12

CSS制作图形

气泡三角形

图片描述

<section id="bubbly" class="code-wrap cf">
      <div class="css-live-wrap">
        
        <summary>利用 border 的 transparent 特性实现</summary>
        <hgroup class="bubbly">
          <h1>.bubbly</h1>
        </hgroup>
      </div>
      <style>
        .bubbly {
          position: absolute;
          top:50%;
          left: 50%;
          transform:translate(-50%,-50%);
          background: #00aabb;
          border-radius: .4em;
          width: 260px;
          padding: 60px 20px;
          text-align: center;
          color: white;
          font-size: 200%;
        }
        .bubbly:after {
          content: '';
          position: absolute;
          bottom: 0;
          left: 50%;
          border: 34px solid transparent;
          border-top-color: #00aabb;
          border-bottom: 0;
          border-left: 0;
          margin: 0 0 -34px -17px;
        }
      </style>
    </section>

梯形

图片描述

<section id="trapezoid" class="code-wrap cf">
      <h2>梯形</h2>
      <div class="css-live-wrap">
        
        <summary>利用伪类加旋转透视实现</summary>
        <hgroup class="trapezoid">
          <h1>.trapezoid</h1>
        </hgroup>
      </div>
      <style>
        .trapezoid{
          position: absolute;
          top:50%;
          left: 50%;
          transform:translate(-50%,-50%);
          width: 160px;
          padding: 60px;
          text-align: center;
          color: white;
          font-size: 200%;
        }
        .trapezoid:before{
          content:"";
          position: absolute;
          top: 0; right: 0; bottom: 0; left: 0;
          transform:perspective(40px) scaleY(1.3) rotateX(5deg);
          transform-origin: bottom;
          background:deeppink;
          z-index:-1;
        }
      </style>
    </section>

爱心

图片描述

<div id="heart"></div>
<pre class="brush:css">
#heart {
    position: relative;
    width: 100px;
    height: 90px;
}
#heart:before,
#heart:after {
    position: absolute;
    content: "";
    left: 50px;
    top: 0;
    width: 50px;
    height: 80px;
    background: red;
    -moz-border-radius: 50px 50px 0 0;
    border-radius: 50px 50px 0 0;
    -webkit-transform: rotate(-45deg);
       -moz-transform: rotate(-45deg);
        -ms-transform: rotate(-45deg);
         -o-transform: rotate(-45deg);
            transform: rotate(-45deg);
    -webkit-transform-origin: 0 100%;
       -moz-transform-origin: 0 100%;
        -ms-transform-origin: 0 100%;
         -o-transform-origin: 0 100%;
            transform-origin: 0 100%;
}
#heart:after {
    left: 0;
    -webkit-transform: rotate(45deg);
       -moz-transform: rotate(45deg);
        -ms-transform: rotate(45deg);
         -o-transform: rotate(45deg);
            transform: rotate(45deg);
    -webkit-transform-origin: 100% 100%;
       -moz-transform-origin: 100% 100%;
        -ms-transform-origin: 100% 100%;
         -o-transform-origin: 100% 100%;
            transform-origin :100% 100%;
}

太极图

图片描述

<div id="yin-yang"></div>
<pre class="brush:css">
#yin-yang {
    width: 96px;
    height: 48px;
    background: #eee;
    border-color: red;
    border-style: solid;
    border-width: 2px 2px 50px 2px;
    border-radius: 100%;
    position: relative;
}

#yin-yang:before {
    content: "";
    position: absolute;
    top: 50%;
    left: 0;
    background: #eee;
    border: 18px solid red;
    border-radius: 100%;
    width: 12px;
    height: 12px;
}

#yin-yang:after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    background: red;
    border: 18px solid #eee;
    border-radius:100%;
    width: 12px;
    height: 12px;
}

折角

图片描述

<div class="css-live-wrap">
        <div class="star" title="致敬《CSS SECRET》作者,此实例是书中例子"></div>
        
        <summary>利用切角、伪类、渐变、旋转实现</summary>
        <hgroup class="corner">
          <h1>.corner</h1>
        </hgroup>
      </div>
      <style>
        .corner{
          position: absolute;
          top:50%;left: 50%;
          transform:translate(-50%,-50%);
          width: 120px;line-height:120px;
          padding:40px;
          text-align: center;
          color: white;
          font-size: 200%;
          background:linear-gradient(-150deg,transparent 1.5em, yellowgreen  0);
          border-radius:.5em;
        }
        .corner:before{
          content: '';
          position: absolute;
          top: 0; right: 0;
          background:
          linear-gradient(to left bottom,transparent 50%, rgba(0,0,0,.2) 0, rgba(0,0,0,.4)) 100% 0 no-repeat;
          width: 1.73em; height: 3em;
          transform: translateY(-1.3em) rotate(-30deg);
          transform-origin: bottom right;
          border-bottom-left-radius: inherit;
          box-shadow: -.2em .2em .3em -.1em rgba(0,0,0,.15);
        }

      </style>

混合模式背景图

图片描述

<div class="css-live-wrap">
        
        <summary>利用渐变实现</summary>
        <hgroup class="colorful-stripe">
          <h1>.colorful-stripe</h1>
        </hgroup>
      </div>
      <style>
        .colorful-stripe {
          position: absolute;
          top: 50%;
          left: 50%;
          transform: translate(-50%, -50%);
          width: 300px;
          line-height: 200px;
          text-align: center;
          color: #fff;
          font-size: 200%;
          border-radius: .5em;
          background:
          linear-gradient(limegreen, transparent), linear-gradient(90deg, skyblue, transparent), linear-gradient(-90deg, coral, transparent);
          background-blend-mode: screen;
        }
      </style>

多云

图片描述

<div class="css-live-wrap">
        
        <summary>利用线性渐变、阴影、缩放实现</summary>
        <hgroup class="cloudy">
          <h1>.cloudy</h1>
        </hgroup>
      </div>
      <style>
        .cloudy{
          position: absolute;
          top: 50%;left: 50%;
          width:200px;height:260px;
          transform: translate(-50%, -50%);
          text-align:center;
          font-size:200%;
          color:#fff;
          background:#2EB5E5;
          border-radius:5px;
        }
        .cloudy:before {
          content: "";
          text-indent:23px;
          font-size:22px;
          line-height:40px;
          color:#333;
          position: absolute;
          height: 50px;width: 50px;
          background: #FFFFFF;
          left:30%;
          top:45%;
          transform: translate(-50%, -50%);
          border-radius: 50%;
          box-shadow:
            #FFFFFF 65px -15px 0 -5px,
            #FFFFFF 25px -25px,
            #FFFFFF 30px 10px,
            #FFFFFF 60px 15px 0 -10px,
            #FFFFFF 85px 5px 0 -5px,
            #C8C8C8 35px -35px,
            #C8C8C8 66px -27px 0 -5px,
            #C8C8C8 91px -10px 0 -8px;
          animation: cloudy 5s ease-in-out infinite;
        }
        .cloudy:after{
          content:"";
          position: absolute;
          top: 80%;left: 50%;
          height: 15px;
          width: 120px;
          background:rgba(0,0,0,.5);
          border-radius: 50%;
          transform: translate(-50%, -50%);
          animation: cloudy_shadow 5s ease-in-out infinite;
        }
      @keyframes cloudy {
        50%{
          transform: translate(-50%, -70%);
        }
        100%{
          transform: translate(-50%, -50%);
        }
      }
      @keyframes cloudy_shadow {
        50%{
          transform: translate(-50%, -50%) scale(0.8);
          background:rgba(0,0,0,.2);
        }
        100%{
          transform: translate(-50%, -50%) scale(1);
            background:rgba(0,0,0,.5);
        }
      }
      </style>

阴影实现多云天气图案

图片描述

<div class="css-live-wrap">
        <summary>多云(cloudy2)(单标签实现)</summary>
        <summary>利用线性渐变、阴影、缩放实现</summary>
        <hgroup class="cloudy2">
          <h1>.cloudy2</h1>
        </hgroup>
      </div>
      <style>
        .cloudy2{
          position: absolute;
          top: 50%;left: 50%;
          width:200px;height:260px;
          transform: translate(-50%, -50%);
          text-align:center;
          font-size:200%;
          color:#fff;
          background:#2EB5E5;
          border-radius:5px;
        }
        .cloudy2:before {
          content: "";
          text-indent:23px;
          font-size:22px;
          line-height:40px;
          color:#333;
          position: absolute;
          height: 50px;width: 50px;
          background: #FFFFFF;
          left:30%;
          top:55%;
          transform: translate(-50%, -50%);
          border-radius: 50%;
          z-index:100;
          box-shadow:
            #FFFFFF 65px -15px 0 -5px,
            #FFFFFF 25px -25px,
            #FFFFFF 30px 10px,
            #FFFFFF 60px 15px 0 -10px,
            #FFFFFF 85px 5px 0 -5px;
          animation: cloudy2 5s ease-in-out infinite;
        }
        .cloudy2:after{
          content:"";
          position: absolute;
          top: 45%;left: 63%;
          height: 60px;
          width: 60px;
          z-index:10;
          background:linear-gradient(180deg,#FE9F38 0%, #F46635 100%);
          border-radius: 50%;
          transform: translate(-50%, -50%);
          box-shadow: 0 0 10px 4px #FFA563;
          animation: cloudy2 10s ease-in-out infinite;
        }
      @keyframes cloudy2 {
        50%{
          transform: translate(-50%, -70%);
        }
        100%{
          transform: translate(-50%, -50%);
        }
      }
      </style>

阴影实现雨天天气图案

图片描述

<div class="css-live-wrap">
        <summary>雨(rainy)</summary>
        <summary>利用线性渐变、阴影、缩放实现</summary>
        <hgroup class="rainy-container">
          <h1>.rainy</h1>
          <div class="rainy"></div>
        </hgroup>
      </div>
      <style>
        .rainy-container {
          position: absolute;
          top: 50%;
          left: 50%;
          width: 200px;
          height: 260px;
          transform: translate(-50%, -50%);
          text-align: center;
          font-size: 200%;
          color: #fff;
          background: #E6E6E6;
          border-radius: 5px;
        }
        .rainy {
          position: absolute;
          width: 3px;
          height: 6px;
          top: 30%;
          left: 50%;
          background: #CCCCCC;
          border-radius: 50%;
          animation: rainy_rain .7s infinite linear;
        }
        .rainy:before {
          content: "";
          color: #333;
          position: absolute;
          height: 50px;
          width: 50px;
          top: 30px;
          left: -40px;
          background: #CCC;
          transform: translate(-50%, -50%);
          border-radius: 50%;
          box-shadow: #CCC 65px -15px 0 -5px, #CCC 25px -25px, #CCC 30px 10px, #CCC 60px 15px 0 -10px, #CCC 85px 5px 0 -5px;
          animation: cloudy 5s ease-in-out infinite;
        }
        .rainy:after {
          content: "";
          position: absolute;
          top: 120px;
          left: 50%;
          height: 15px;
          width: 120px;
          background: rgba(0, 0, 0, .5);
          border-radius: 50%;
          transform: translate(-50%, -50%);
          animation: cloudy_shadow 5s ease-in-out infinite;
        }
        @keyframes cloudy {
          50% {
            transform: translate(-50%, -70%);
          }
          100% {
            transform: translate(-50%, -50%);
          }
        }
        @keyframes cloudy_shadow {
          50% {
            transform: translate(-50%, -50%) scale(0.8);
            background: rgba(0, 0, 0, .2);
          }
          100% {
            transform: translate(-50%, -50%) scale(1);
            background: rgba(0, 0, 0, .5);
          }
        }
        @keyframes rainy_rain {
          0% {
            box-shadow: rgba(0, 0, 0, 0) -10px 30px, rgba(0, 0, 0, 0) 40px 40px, rgba(0, 0, 0, .3) -50px 75px, rgba(0, 0, 0, .3) 55px 50px, rgba(0, 0, 0, .3) -18px 100px, rgba(0, 0, 0, .3) 12px 95px, rgba(0, 0, 0, .3) -31px 45px, rgba(0, 0, 0, .3) 30px 35px;
          }
          25% {
            box-shadow: rgba(0, 0, 0, .3) -10px 45px, rgba(0, 0, 0, .3) 40px 60px, rgba(0, 0, 0, .3) -50px 90px, rgba(0, 0, 0, .3) 55px 65px, rgba(0, 0, 0, 0) -18px 120px, rgba(0, 0, 0, 0) 12px 120px, rgba(0, 0, 0, .3) -31px 70px, rgba(0, 0, 0, .3) 30px 60px;
          }
          26% {
            box-shadow: rgba(0, 0, 0, .3) -10px 45px, rgba(0, 0, 0, .3) 40px 60px, rgba(0, 0, 0, .3) -50px 90px, rgba(0, 0, 0, .3) 55px 65px, rgba(0, 0, 0, 0) -18px 40px, rgba(0, 0, 0, 0) 12px 20px, rgba(0, 0, 0, .3) -31px 70px, rgba(0, 0, 0, .3) 30px 60px;
          }
          50% {
            box-shadow: rgba(0, 0, 0, .3) -10px 70px, rgba(0, 0, 0, .3) 40px 80px, rgba(0, 0, 0, 0) -50px 100px, rgba(0, 0, 0, .3) 55px 80px, rgba(0, 0, 0, .3) -18px 60px, rgba(0, 0, 0, .3) 12px 45px, rgba(0, 0, 0, .3) -31px 95px, rgba(0, 0, 0, .3) 30px 85px;
          }
          51% {
            box-shadow: rgba(0, 0, 0, .3) -10px 70px, rgba(0, 0, 0, .3) 40px 80px, rgba(0, 0, 0, 0) -50px 45px, rgba(0, 0, 0, .3) 55px 80px, rgba(0, 0, 0, .3) -18px 60px, rgba(0, 0, 0, .3) 12px 45px, rgba(0, 0, 0, .3) -31px 95px, rgba(0, 0, 0, .3) 30px 85px;
          }
          75% {
            box-shadow: rgba(0, 0, 0, .3) -10px 95px, rgba(0, 0, 0, .3) 40px 100px, rgba(0, 0, 0, .3) -50px 60px, rgba(0, 0, 0, 0) 55px 95px, rgba(0, 0, 0, .3) -18px 80px, rgba(0, 0, 0, .3) 12px 70px, rgba(0, 0, 0, 0) -31px 120px, rgba(0, 0, 0, 0) 30px 110px;
          }
          76% {
            box-shadow: rgba(0, 0, 0, .3) -10px 95px, rgba(0, 0, 0, .3) 40px 100px, rgba(0, 0, 0, .3) -50px 60px, rgba(0, 0, 0, 0) 55px 35px, rgba(0, 0, 0, .3) -18px 80px, rgba(0, 0, 0, .3) 12px 70px, rgba(0, 0, 0, 0) -31px 25px, rgba(0, 0, 0, 0) 30px 15px;
          }
          100% {
            box-shadow: rgba(0, 0, 0, 0) -10px 120px, rgba(0, 0, 0, 0) 40px 120px, rgba(0, 0, 0, .3) -50px 75px, rgba(0, 0, 0, .3) 55px 50px, rgba(0, 0, 0, .3) -18px 100px, rgba(0, 0, 0, .3) 12px 95px, rgba(0, 0, 0, .3) -31px 45px, rgba(0, 0, 0, .3) 30px 35px;
          }
        }
      </style>

彩虹天气图案

图片描述

<div class="css-live-wrap">
        <summary>彩虹(rainbow)</summary>
        <summary>利用border、box-shadow 实现</summary>
        <hgroup class="rainbow-container">
          <h1>.rainbow</h1>
          <div class="rainbow"></div>
        </hgroup>
      </div>
      <style>
        .rainbow-container{
          position: absolute;
          top: 50%;left: 50%;
          width:200px;height:260px;
          transform: translate(-50%, -50%);
          text-align:center;
          font-size:200%;
          color:#fff;
          background:#F3D166;
          border-radius:5px;
        }
        .rainbow{
          position:absolute;
          top: 50%;
          left: 50%;
          transform: translate(-50%, -50%);
          height: 1px;width: 1px;
        }
        .rainbow:before{
          content:"";
          position:absolute;
          top: 50%;left: 50%;
          transform: translate(-50%, -50%) rotate(45deg);
          height: 70px;width: 70px;
          border-radius: 100px 0 0 0;
          box-shadow:
            #F44336 -2px -2px 0 1px,
            #FF9800 -4px -4px 0 3px,
            #FFEB3B -6px -6px 0 5px,
            #8BC34A -8px -8px 0 7px,
            #00BCD4 -10px -10px 0 9px,
            #2196F3 -12px -12px 0 11px,
            #9C27B0 -14px -14px 0 13px;
          animation: rainbow 5s ease-in-out infinite;
        }
        .rainbow:after{
          content: "";
          position: absolute;
          top: 70px;
          left: 50%;
          height: 15px;
          width: 120px;
          background: rgba(0, 0, 0, .5);
          border-radius: 50%;
          transform: translate(-50%, -50%);
          animation: cloudy_shadow 5s ease-in-out infinite;
        }
        @keyframes rainbow {
          50% {
            transform: translate(-50%, -55%) rotate(30deg);
          }
          100% {
            transform: translate(-50%, -50%) rotate(45deg);
          }
        }
        @keyframes cloudy_shadow {
          50% {
            transform: translate(-50%, -50%) scale(0.8);
            background: rgba(0, 0, 0, .2);
          }
          100% {
            transform: translate(-50%, -50%) scale(1);
            background: rgba(0, 0, 0, .5);
          }
        }

雪天天气图案

图片描述

<div class="css-live-wrap">
        <summary>雪(snowy)</summary>
        <summary>利用阴影实现</summary>
        <hgroup class="snowy-container">
          <h1>.snowy</h1>
          <div class="snowy"></div>
        </hgroup>
      </div>
      <style>
        .snowy-container {
          position: absolute;
          top: 50%;
          left: 50%;
          width: 200px;
          height: 260px;
          transform: translate(-50%, -50%);
          text-align: center;
          font-size: 200%;
          color: #fff;
          background: #607D8B;
          border-radius: 5px;
        }
        .snowy {
          position: absolute;
          width: 4px;
          height: 4px;
          border-radius:50%;
          top: 30%;
          left: 50%;
          background: #fff;
          border-radius: 50%;
          animation: snowy_rain 2s infinite linear;
        }
        .snowy:before {
          content: "";
          color: #333;
          position: absolute;
          height: 50px;
          width: 50px;
          top: 30px;
          left: -40px;
          background: #eee;
          transform: translate(-50%, -50%);
          border-radius: 50%;
          box-shadow:
            #eee 65px -15px 0 -5px,
            #eee 25px -25px,
            #eee 30px 10px,
            #eee 60px 15px 0 -10px,
            #eee 85px 5px 0 -5px;
          animation: cloudy 5s ease-in-out infinite;
        }
        .snowy:after {
          content: "";
          position: absolute;
          top: 120px;
          left: 50%;
          height: 15px;
          width: 120px;
          background: rgba(0, 0, 0, .5);
          border-radius: 50%;
          transform: translate(-50%, -50%);
          animation: cloudy_shadow 5s ease-in-out infinite;
        }
        @keyframes cloudy {
          50% {
            transform: translate(-50%, -70%);
          }
          100% {
            transform: translate(-50%, -50%);
          }
        }
        @keyframes cloudy_shadow {
          50% {
            transform: translate(-50%, -50%) scale(0.8);
            background: rgba(0, 0, 0, .2);
          }
          100% {
            transform: translate(-50%, -50%) scale(1);
            background: rgba(0, 0, 0, .5);
          }
        }
        @keyframes snowy_rain {
          0% {
            box-shadow:
              rgba(255, 255, 255, 0) -10px 30px,
              rgba(255, 255, 255, 0) 40px 40px,
              rgba(255, 255, 255, .6) -50px 75px,
              rgba(255, 255, 255, .6) 55px 50px,
              rgba(255, 255, 255, .6) -18px 100px,
              rgba(255, 255, 255, .6) 12px 95px,
              rgba(255, 255, 255, .6) -31px 45px,
              rgba(255, 255, 255, .6) 30px 35px;
          }
          25% {
            box-shadow:
              rgba(255, 255, 255, .6) -10px 45px,
              rgba(255, 255, 255, .6) 40px 60px,
              rgba(255, 255, 255, .6) -50px 90px,
              rgba(255, 255, 255, .6) 55px 65px,
              rgba(255, 255, 255, 0) -18px 120px,
              rgba(255, 255, 255, 0) 12px 120px,
              rgba(255, 255, 255, .6) -31px 70px,
              rgba(255, 255, 255, .6) 30px 60px;
          }
          26% {
            box-shadow:
              rgba(255, 255, 255, .6) -10px 45px,
              rgba(255, 255, 255, .6) 40px 60px,
              rgba(255, 255, 255, .6) -50px 90px,
              rgba(255, 255, 255, .6) 55px 65px,
              rgba(255, 255, 255, 0) -18px 40px,
              rgba(255, 255, 255, 0) 12px 20px,
              rgba(255, 255, 255, .6) -31px 70px,
              rgba(255, 255, 255, .6) 30px 60px;
          }
          50% {
            box-shadow:
              rgba(255, 255, 255, .6) -10px 70px,
              rgba(255, 255, 255, .6) 40px 80px,
              rgba(255, 255, 255, 0) -50px 100px,
              rgba(255, 255, 255, .6) 55px 80px,
              rgba(255, 255, 255, .6) -18px 60px,
              rgba(255, 255, 255, .6) 12px 45px,
              rgba(255, 255, 255, .6) -31px 95px,
              rgba(255, 255, 255, .6) 30px 85px;
          }
          51% {
            box-shadow:
              rgba(255, 255, 255, .6) -10px 70px,
              rgba(255, 255, 255, .6) 40px 80px,
              rgba(255, 255, 255, 0) -50px 45px,
              rgba(255, 255, 255, .6) 55px 80px,
              rgba(255, 255, 255, .6) -18px 60px,
              rgba(255, 255, 255, .6) 12px 45px,
              rgba(255, 255, 255, .6) -31px 95px,
              rgba(255, 255, 255, .6) 30px 85px;
          }
          75% {
            box-shadow:
              rgba(255, 255, 255, .6) -10px 95px,
              rgba(255, 255, 255, .6) 40px 100px,
              rgba(255, 255, 255, .6) -50px 60px,
              rgba(255, 255, 255, 0) 55px 95px,
              rgba(255, 255, 255, .6) -18px 80px,
              rgba(255, 255, 255, .6) 12px 70px,
              rgba(255, 255, 255, 0) -31px 120px,
              rgba(255, 255, 255, 0) 30px 110px;
          }
          76% {
            box-shadow:
              rgba(255, 255, 255, .6) -10px 95px,
              rgba(255, 255, 255, .6) 40px 100px,
              rgba(255, 255, 255, .6) -50px 60px,
              rgba(255, 255, 255, 0) 55px 35px,
              rgba(255, 255, 255, .6) -18px 80px,
              rgba(255, 255, 255, .6) 12px 70px,
              rgba(255, 255, 255, 0) -31px 25px,
              rgba(255, 255, 255, 0) 30px 15px;
          }
          100% {
            box-shadow:
              rgba(255, 255, 255, 0) -10px 120px,
              rgba(255, 255, 255, 0) 40px 120px,
              rgba(255, 255, 255, .6) -50px 75px,
              rgba(255, 255, 255, .6) 55px 50px,
              rgba(255, 255, 255, .6) -18px 100px,
              rgba(255, 255, 255, .6) 12px 95px,
              rgba(255, 255, 255, .6) -31px 45px,
              rgba(255, 255, 255, .6) 30px 35px;
          }
        }
      </style>

chrome 浏览器图标

图片描述

<div class="css-live-wrap">
        <summary>Chrome(单标签实现)</summary>
        <summary>利用渐变实现</summary>
        <hgroup class="Chrome">
          <h1></h1>
        </hgroup>
      </div>
      <style>
        .Chrome{
          position: absolute;
          top: 50%;left: 50%;
          width: 180px;height: 180px;
          transform: translate(-50%, -50%);
          box-shadow:0 0px 4px #999,0 0 2px #ddd inset;
          border-radius:50%;
          background-image:
            radial-gradient(#4FACF5 0%,#2196F3 28%, transparent 28%),
            radial-gradient(#fff 33%, transparent 33%),
            linear-gradient(-50deg,#FFEB3B 34%, transparent 34%),
            linear-gradient(60deg,#4CAF50 33%, transparent 33%),
            linear-gradient(180deg,#FF756B 0%, #F44336 30%, transparent 30%),
            linear-gradient(-120deg,#FFEB3B 40%, transparent 40%),
            linear-gradient(-60deg,#FFEB3B 30%, transparent 30%),
            linear-gradient(0deg,#4CAF50 45%, transparent 45%),
            linear-gradient(60deg,#4CAF50 30%, transparent 30%),
            linear-gradient(120deg,#F44336 50%, transparent 50%),
            linear-gradient(180deg,#F44336 30%, transparent 30%);
          background-position:0 0;
        }
      </style>

IE 浏览器图标

图片描述

<div class="css-live-wrap">
        <summary>IE(单标签实现)</summary>
        <summary>利用渐变、多重阴影实现</summary>
        <hgroup class="IE">
          <h1></h1>
        </hgroup>
      </div>
      <style>
        .IE{
          position: absolute;
          top: 50%;left: 50%;
          width:200px; height:200px;
          transform: translate(-50%, -50%);
          border-radius:50%;
          /* box-shadow:1px 2px 1px #ddd; */
          background-image:
             radial-gradient(#fff 38%, transparent 38%),
             radial-gradient(#09C 0%, #09C 100%);

        }
        .IE:before{
          content: "";
          width:285px;
          height:122px;
          background:none;
          border-radius:100%;
          position:absolute;
          top:33px;
          left:-45px;
          margin:auto;
          box-shadow:
            inset 0 12px 0 13px  #09c,
            -35px -8px 0 -5px #fff;
          transform: rotate(-35deg);
        }
        .IE:after {
          content: "";
          width: 120px;
          height: 25px;
          background: #09c;
          position: absolute;
          top: 80px;
          left: 0;
          right: 0;
          margin: auto;
          box-shadow: 50px 23px 0 -2px #fff
        }
      </style>

safari 浏览器图标

图片描述

<div class="css-live-wrap">
        <summary>safari(单标签实现)</summary>
        <summary>利用渐变、border、旋转实现</summary>
        <hgroup class="safari">
          <h1></h1>
        </hgroup>
      </div>
      <style>
        .safari{
          position: absolute;
          top: 50%;left: 50%;
          width:200px; height:200px;
          transform: translate(-50%, -50%);
          border-radius:50%;
          border:5px solid #E8E8E8;
          box-shadow:
            -1px 3px 1px 2px #999,
            -1px 3px 1px 2px #999 inset;
          background-image:
            radial-gradient(transparent 30%,#fff 30%,#fff 34%, transparent 34%),
            linear-gradient(rgba(255,255,255,.8) 100%, transparent 100%),
            linear-gradient(rgba(255,255,255,.8) 100%, transparent 100%),
            linear-gradient(rgba(255,255,255,.8) 100%, transparent 100%),
            linear-gradient(rgba(255,255,255,.8) 100%, transparent 100%),
            linear-gradient(#1DE3FF 0%, #1F52EF 100%);
          background-size: 50% 50%,20px 2px,20px 2px,2px 20px,2px 20px,100%,100%;
          background-repeat:no-repeat;
          background-position:center center, 175px center,5px center,center 175px,center 5px,0 0;

        }
        .safari::before{
          content:"";
          position: absolute;
          top: 10px;left: 50%;
          border-radius:10px;
          /* width:20px; height:80px; */
          border-bottom:100px solid rgba(255,255,255,.9);
          border-left:10px solid transparent;
          border-right:10px solid transparent;
          transform-origin:center 90px;
          z-index:-1;
          transform:translate(-50%, 0%) rotate(40deg);
        }
        .safari::after{
          content:"";
          position: absolute;
          top: 10px;left: 50%;
          border-radius:10px;
          /* width:20px; height:80px; */
          border-bottom:100px solid rgba(255,0,0,.9);
          border-left:10px solid transparent;
          border-right:10px solid transparent;
          transform-origin:center 90px;
          transform:translate(-50%, 0%) rotate(220deg);
        }
        .safari:hover::before{
            transition:transform 1s;
            transform:translate(-50%, 0%) rotate(70deg);
        }
        .safari:hover::after{
            transition:transform 1s;
            transform:translate(-50%, 0%) rotate(250deg);
        }
      </style>
查看原文

xiayefeng 赞了文章 · 2019-01-12

CSS制作图形

气泡三角形

图片描述

<section id="bubbly" class="code-wrap cf">
      <div class="css-live-wrap">
        
        <summary>利用 border 的 transparent 特性实现</summary>
        <hgroup class="bubbly">
          <h1>.bubbly</h1>
        </hgroup>
      </div>
      <style>
        .bubbly {
          position: absolute;
          top:50%;
          left: 50%;
          transform:translate(-50%,-50%);
          background: #00aabb;
          border-radius: .4em;
          width: 260px;
          padding: 60px 20px;
          text-align: center;
          color: white;
          font-size: 200%;
        }
        .bubbly:after {
          content: '';
          position: absolute;
          bottom: 0;
          left: 50%;
          border: 34px solid transparent;
          border-top-color: #00aabb;
          border-bottom: 0;
          border-left: 0;
          margin: 0 0 -34px -17px;
        }
      </style>
    </section>

梯形

图片描述

<section id="trapezoid" class="code-wrap cf">
      <h2>梯形</h2>
      <div class="css-live-wrap">
        
        <summary>利用伪类加旋转透视实现</summary>
        <hgroup class="trapezoid">
          <h1>.trapezoid</h1>
        </hgroup>
      </div>
      <style>
        .trapezoid{
          position: absolute;
          top:50%;
          left: 50%;
          transform:translate(-50%,-50%);
          width: 160px;
          padding: 60px;
          text-align: center;
          color: white;
          font-size: 200%;
        }
        .trapezoid:before{
          content:"";
          position: absolute;
          top: 0; right: 0; bottom: 0; left: 0;
          transform:perspective(40px) scaleY(1.3) rotateX(5deg);
          transform-origin: bottom;
          background:deeppink;
          z-index:-1;
        }
      </style>
    </section>

爱心

图片描述

<div id="heart"></div>
<pre class="brush:css">
#heart {
    position: relative;
    width: 100px;
    height: 90px;
}
#heart:before,
#heart:after {
    position: absolute;
    content: "";
    left: 50px;
    top: 0;
    width: 50px;
    height: 80px;
    background: red;
    -moz-border-radius: 50px 50px 0 0;
    border-radius: 50px 50px 0 0;
    -webkit-transform: rotate(-45deg);
       -moz-transform: rotate(-45deg);
        -ms-transform: rotate(-45deg);
         -o-transform: rotate(-45deg);
            transform: rotate(-45deg);
    -webkit-transform-origin: 0 100%;
       -moz-transform-origin: 0 100%;
        -ms-transform-origin: 0 100%;
         -o-transform-origin: 0 100%;
            transform-origin: 0 100%;
}
#heart:after {
    left: 0;
    -webkit-transform: rotate(45deg);
       -moz-transform: rotate(45deg);
        -ms-transform: rotate(45deg);
         -o-transform: rotate(45deg);
            transform: rotate(45deg);
    -webkit-transform-origin: 100% 100%;
       -moz-transform-origin: 100% 100%;
        -ms-transform-origin: 100% 100%;
         -o-transform-origin: 100% 100%;
            transform-origin :100% 100%;
}

太极图

图片描述

<div id="yin-yang"></div>
<pre class="brush:css">
#yin-yang {
    width: 96px;
    height: 48px;
    background: #eee;
    border-color: red;
    border-style: solid;
    border-width: 2px 2px 50px 2px;
    border-radius: 100%;
    position: relative;
}

#yin-yang:before {
    content: "";
    position: absolute;
    top: 50%;
    left: 0;
    background: #eee;
    border: 18px solid red;
    border-radius: 100%;
    width: 12px;
    height: 12px;
}

#yin-yang:after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    background: red;
    border: 18px solid #eee;
    border-radius:100%;
    width: 12px;
    height: 12px;
}

折角

图片描述

<div class="css-live-wrap">
        <div class="star" title="致敬《CSS SECRET》作者,此实例是书中例子"></div>
        
        <summary>利用切角、伪类、渐变、旋转实现</summary>
        <hgroup class="corner">
          <h1>.corner</h1>
        </hgroup>
      </div>
      <style>
        .corner{
          position: absolute;
          top:50%;left: 50%;
          transform:translate(-50%,-50%);
          width: 120px;line-height:120px;
          padding:40px;
          text-align: center;
          color: white;
          font-size: 200%;
          background:linear-gradient(-150deg,transparent 1.5em, yellowgreen  0);
          border-radius:.5em;
        }
        .corner:before{
          content: '';
          position: absolute;
          top: 0; right: 0;
          background:
          linear-gradient(to left bottom,transparent 50%, rgba(0,0,0,.2) 0, rgba(0,0,0,.4)) 100% 0 no-repeat;
          width: 1.73em; height: 3em;
          transform: translateY(-1.3em) rotate(-30deg);
          transform-origin: bottom right;
          border-bottom-left-radius: inherit;
          box-shadow: -.2em .2em .3em -.1em rgba(0,0,0,.15);
        }

      </style>

混合模式背景图

图片描述

<div class="css-live-wrap">
        
        <summary>利用渐变实现</summary>
        <hgroup class="colorful-stripe">
          <h1>.colorful-stripe</h1>
        </hgroup>
      </div>
      <style>
        .colorful-stripe {
          position: absolute;
          top: 50%;
          left: 50%;
          transform: translate(-50%, -50%);
          width: 300px;
          line-height: 200px;
          text-align: center;
          color: #fff;
          font-size: 200%;
          border-radius: .5em;
          background:
          linear-gradient(limegreen, transparent), linear-gradient(90deg, skyblue, transparent), linear-gradient(-90deg, coral, transparent);
          background-blend-mode: screen;
        }
      </style>

多云

图片描述

<div class="css-live-wrap">
        
        <summary>利用线性渐变、阴影、缩放实现</summary>
        <hgroup class="cloudy">
          <h1>.cloudy</h1>
        </hgroup>
      </div>
      <style>
        .cloudy{
          position: absolute;
          top: 50%;left: 50%;
          width:200px;height:260px;
          transform: translate(-50%, -50%);
          text-align:center;
          font-size:200%;
          color:#fff;
          background:#2EB5E5;
          border-radius:5px;
        }
        .cloudy:before {
          content: "";
          text-indent:23px;
          font-size:22px;
          line-height:40px;
          color:#333;
          position: absolute;
          height: 50px;width: 50px;
          background: #FFFFFF;
          left:30%;
          top:45%;
          transform: translate(-50%, -50%);
          border-radius: 50%;
          box-shadow:
            #FFFFFF 65px -15px 0 -5px,
            #FFFFFF 25px -25px,
            #FFFFFF 30px 10px,
            #FFFFFF 60px 15px 0 -10px,
            #FFFFFF 85px 5px 0 -5px,
            #C8C8C8 35px -35px,
            #C8C8C8 66px -27px 0 -5px,
            #C8C8C8 91px -10px 0 -8px;
          animation: cloudy 5s ease-in-out infinite;
        }
        .cloudy:after{
          content:"";
          position: absolute;
          top: 80%;left: 50%;
          height: 15px;
          width: 120px;
          background:rgba(0,0,0,.5);
          border-radius: 50%;
          transform: translate(-50%, -50%);
          animation: cloudy_shadow 5s ease-in-out infinite;
        }
      @keyframes cloudy {
        50%{
          transform: translate(-50%, -70%);
        }
        100%{
          transform: translate(-50%, -50%);
        }
      }
      @keyframes cloudy_shadow {
        50%{
          transform: translate(-50%, -50%) scale(0.8);
          background:rgba(0,0,0,.2);
        }
        100%{
          transform: translate(-50%, -50%) scale(1);
            background:rgba(0,0,0,.5);
        }
      }
      </style>

阴影实现多云天气图案

图片描述

<div class="css-live-wrap">
        <summary>多云(cloudy2)(单标签实现)</summary>
        <summary>利用线性渐变、阴影、缩放实现</summary>
        <hgroup class="cloudy2">
          <h1>.cloudy2</h1>
        </hgroup>
      </div>
      <style>
        .cloudy2{
          position: absolute;
          top: 50%;left: 50%;
          width:200px;height:260px;
          transform: translate(-50%, -50%);
          text-align:center;
          font-size:200%;
          color:#fff;
          background:#2EB5E5;
          border-radius:5px;
        }
        .cloudy2:before {
          content: "";
          text-indent:23px;
          font-size:22px;
          line-height:40px;
          color:#333;
          position: absolute;
          height: 50px;width: 50px;
          background: #FFFFFF;
          left:30%;
          top:55%;
          transform: translate(-50%, -50%);
          border-radius: 50%;
          z-index:100;
          box-shadow:
            #FFFFFF 65px -15px 0 -5px,
            #FFFFFF 25px -25px,
            #FFFFFF 30px 10px,
            #FFFFFF 60px 15px 0 -10px,
            #FFFFFF 85px 5px 0 -5px;
          animation: cloudy2 5s ease-in-out infinite;
        }
        .cloudy2:after{
          content:"";
          position: absolute;
          top: 45%;left: 63%;
          height: 60px;
          width: 60px;
          z-index:10;
          background:linear-gradient(180deg,#FE9F38 0%, #F46635 100%);
          border-radius: 50%;
          transform: translate(-50%, -50%);
          box-shadow: 0 0 10px 4px #FFA563;
          animation: cloudy2 10s ease-in-out infinite;
        }
      @keyframes cloudy2 {
        50%{
          transform: translate(-50%, -70%);
        }
        100%{
          transform: translate(-50%, -50%);
        }
      }
      </style>

阴影实现雨天天气图案

图片描述

<div class="css-live-wrap">
        <summary>雨(rainy)</summary>
        <summary>利用线性渐变、阴影、缩放实现</summary>
        <hgroup class="rainy-container">
          <h1>.rainy</h1>
          <div class="rainy"></div>
        </hgroup>
      </div>
      <style>
        .rainy-container {
          position: absolute;
          top: 50%;
          left: 50%;
          width: 200px;
          height: 260px;
          transform: translate(-50%, -50%);
          text-align: center;
          font-size: 200%;
          color: #fff;
          background: #E6E6E6;
          border-radius: 5px;
        }
        .rainy {
          position: absolute;
          width: 3px;
          height: 6px;
          top: 30%;
          left: 50%;
          background: #CCCCCC;
          border-radius: 50%;
          animation: rainy_rain .7s infinite linear;
        }
        .rainy:before {
          content: "";
          color: #333;
          position: absolute;
          height: 50px;
          width: 50px;
          top: 30px;
          left: -40px;
          background: #CCC;
          transform: translate(-50%, -50%);
          border-radius: 50%;
          box-shadow: #CCC 65px -15px 0 -5px, #CCC 25px -25px, #CCC 30px 10px, #CCC 60px 15px 0 -10px, #CCC 85px 5px 0 -5px;
          animation: cloudy 5s ease-in-out infinite;
        }
        .rainy:after {
          content: "";
          position: absolute;
          top: 120px;
          left: 50%;
          height: 15px;
          width: 120px;
          background: rgba(0, 0, 0, .5);
          border-radius: 50%;
          transform: translate(-50%, -50%);
          animation: cloudy_shadow 5s ease-in-out infinite;
        }
        @keyframes cloudy {
          50% {
            transform: translate(-50%, -70%);
          }
          100% {
            transform: translate(-50%, -50%);
          }
        }
        @keyframes cloudy_shadow {
          50% {
            transform: translate(-50%, -50%) scale(0.8);
            background: rgba(0, 0, 0, .2);
          }
          100% {
            transform: translate(-50%, -50%) scale(1);
            background: rgba(0, 0, 0, .5);
          }
        }
        @keyframes rainy_rain {
          0% {
            box-shadow: rgba(0, 0, 0, 0) -10px 30px, rgba(0, 0, 0, 0) 40px 40px, rgba(0, 0, 0, .3) -50px 75px, rgba(0, 0, 0, .3) 55px 50px, rgba(0, 0, 0, .3) -18px 100px, rgba(0, 0, 0, .3) 12px 95px, rgba(0, 0, 0, .3) -31px 45px, rgba(0, 0, 0, .3) 30px 35px;
          }
          25% {
            box-shadow: rgba(0, 0, 0, .3) -10px 45px, rgba(0, 0, 0, .3) 40px 60px, rgba(0, 0, 0, .3) -50px 90px, rgba(0, 0, 0, .3) 55px 65px, rgba(0, 0, 0, 0) -18px 120px, rgba(0, 0, 0, 0) 12px 120px, rgba(0, 0, 0, .3) -31px 70px, rgba(0, 0, 0, .3) 30px 60px;
          }
          26% {
            box-shadow: rgba(0, 0, 0, .3) -10px 45px, rgba(0, 0, 0, .3) 40px 60px, rgba(0, 0, 0, .3) -50px 90px, rgba(0, 0, 0, .3) 55px 65px, rgba(0, 0, 0, 0) -18px 40px, rgba(0, 0, 0, 0) 12px 20px, rgba(0, 0, 0, .3) -31px 70px, rgba(0, 0, 0, .3) 30px 60px;
          }
          50% {
            box-shadow: rgba(0, 0, 0, .3) -10px 70px, rgba(0, 0, 0, .3) 40px 80px, rgba(0, 0, 0, 0) -50px 100px, rgba(0, 0, 0, .3) 55px 80px, rgba(0, 0, 0, .3) -18px 60px, rgba(0, 0, 0, .3) 12px 45px, rgba(0, 0, 0, .3) -31px 95px, rgba(0, 0, 0, .3) 30px 85px;
          }
          51% {
            box-shadow: rgba(0, 0, 0, .3) -10px 70px, rgba(0, 0, 0, .3) 40px 80px, rgba(0, 0, 0, 0) -50px 45px, rgba(0, 0, 0, .3) 55px 80px, rgba(0, 0, 0, .3) -18px 60px, rgba(0, 0, 0, .3) 12px 45px, rgba(0, 0, 0, .3) -31px 95px, rgba(0, 0, 0, .3) 30px 85px;
          }
          75% {
            box-shadow: rgba(0, 0, 0, .3) -10px 95px, rgba(0, 0, 0, .3) 40px 100px, rgba(0, 0, 0, .3) -50px 60px, rgba(0, 0, 0, 0) 55px 95px, rgba(0, 0, 0, .3) -18px 80px, rgba(0, 0, 0, .3) 12px 70px, rgba(0, 0, 0, 0) -31px 120px, rgba(0, 0, 0, 0) 30px 110px;
          }
          76% {
            box-shadow: rgba(0, 0, 0, .3) -10px 95px, rgba(0, 0, 0, .3) 40px 100px, rgba(0, 0, 0, .3) -50px 60px, rgba(0, 0, 0, 0) 55px 35px, rgba(0, 0, 0, .3) -18px 80px, rgba(0, 0, 0, .3) 12px 70px, rgba(0, 0, 0, 0) -31px 25px, rgba(0, 0, 0, 0) 30px 15px;
          }
          100% {
            box-shadow: rgba(0, 0, 0, 0) -10px 120px, rgba(0, 0, 0, 0) 40px 120px, rgba(0, 0, 0, .3) -50px 75px, rgba(0, 0, 0, .3) 55px 50px, rgba(0, 0, 0, .3) -18px 100px, rgba(0, 0, 0, .3) 12px 95px, rgba(0, 0, 0, .3) -31px 45px, rgba(0, 0, 0, .3) 30px 35px;
          }
        }
      </style>

彩虹天气图案

图片描述

<div class="css-live-wrap">
        <summary>彩虹(rainbow)</summary>
        <summary>利用border、box-shadow 实现</summary>
        <hgroup class="rainbow-container">
          <h1>.rainbow</h1>
          <div class="rainbow"></div>
        </hgroup>
      </div>
      <style>
        .rainbow-container{
          position: absolute;
          top: 50%;left: 50%;
          width:200px;height:260px;
          transform: translate(-50%, -50%);
          text-align:center;
          font-size:200%;
          color:#fff;
          background:#F3D166;
          border-radius:5px;
        }
        .rainbow{
          position:absolute;
          top: 50%;
          left: 50%;
          transform: translate(-50%, -50%);
          height: 1px;width: 1px;
        }
        .rainbow:before{
          content:"";
          position:absolute;
          top: 50%;left: 50%;
          transform: translate(-50%, -50%) rotate(45deg);
          height: 70px;width: 70px;
          border-radius: 100px 0 0 0;
          box-shadow:
            #F44336 -2px -2px 0 1px,
            #FF9800 -4px -4px 0 3px,
            #FFEB3B -6px -6px 0 5px,
            #8BC34A -8px -8px 0 7px,
            #00BCD4 -10px -10px 0 9px,
            #2196F3 -12px -12px 0 11px,
            #9C27B0 -14px -14px 0 13px;
          animation: rainbow 5s ease-in-out infinite;
        }
        .rainbow:after{
          content: "";
          position: absolute;
          top: 70px;
          left: 50%;
          height: 15px;
          width: 120px;
          background: rgba(0, 0, 0, .5);
          border-radius: 50%;
          transform: translate(-50%, -50%);
          animation: cloudy_shadow 5s ease-in-out infinite;
        }
        @keyframes rainbow {
          50% {
            transform: translate(-50%, -55%) rotate(30deg);
          }
          100% {
            transform: translate(-50%, -50%) rotate(45deg);
          }
        }
        @keyframes cloudy_shadow {
          50% {
            transform: translate(-50%, -50%) scale(0.8);
            background: rgba(0, 0, 0, .2);
          }
          100% {
            transform: translate(-50%, -50%) scale(1);
            background: rgba(0, 0, 0, .5);
          }
        }

雪天天气图案

图片描述

<div class="css-live-wrap">
        <summary>雪(snowy)</summary>
        <summary>利用阴影实现</summary>
        <hgroup class="snowy-container">
          <h1>.snowy</h1>
          <div class="snowy"></div>
        </hgroup>
      </div>
      <style>
        .snowy-container {
          position: absolute;
          top: 50%;
          left: 50%;
          width: 200px;
          height: 260px;
          transform: translate(-50%, -50%);
          text-align: center;
          font-size: 200%;
          color: #fff;
          background: #607D8B;
          border-radius: 5px;
        }
        .snowy {
          position: absolute;
          width: 4px;
          height: 4px;
          border-radius:50%;
          top: 30%;
          left: 50%;
          background: #fff;
          border-radius: 50%;
          animation: snowy_rain 2s infinite linear;
        }
        .snowy:before {
          content: "";
          color: #333;
          position: absolute;
          height: 50px;
          width: 50px;
          top: 30px;
          left: -40px;
          background: #eee;
          transform: translate(-50%, -50%);
          border-radius: 50%;
          box-shadow:
            #eee 65px -15px 0 -5px,
            #eee 25px -25px,
            #eee 30px 10px,
            #eee 60px 15px 0 -10px,
            #eee 85px 5px 0 -5px;
          animation: cloudy 5s ease-in-out infinite;
        }
        .snowy:after {
          content: "";
          position: absolute;
          top: 120px;
          left: 50%;
          height: 15px;
          width: 120px;
          background: rgba(0, 0, 0, .5);
          border-radius: 50%;
          transform: translate(-50%, -50%);
          animation: cloudy_shadow 5s ease-in-out infinite;
        }
        @keyframes cloudy {
          50% {
            transform: translate(-50%, -70%);
          }
          100% {
            transform: translate(-50%, -50%);
          }
        }
        @keyframes cloudy_shadow {
          50% {
            transform: translate(-50%, -50%) scale(0.8);
            background: rgba(0, 0, 0, .2);
          }
          100% {
            transform: translate(-50%, -50%) scale(1);
            background: rgba(0, 0, 0, .5);
          }
        }
        @keyframes snowy_rain {
          0% {
            box-shadow:
              rgba(255, 255, 255, 0) -10px 30px,
              rgba(255, 255, 255, 0) 40px 40px,
              rgba(255, 255, 255, .6) -50px 75px,
              rgba(255, 255, 255, .6) 55px 50px,
              rgba(255, 255, 255, .6) -18px 100px,
              rgba(255, 255, 255, .6) 12px 95px,
              rgba(255, 255, 255, .6) -31px 45px,
              rgba(255, 255, 255, .6) 30px 35px;
          }
          25% {
            box-shadow:
              rgba(255, 255, 255, .6) -10px 45px,
              rgba(255, 255, 255, .6) 40px 60px,
              rgba(255, 255, 255, .6) -50px 90px,
              rgba(255, 255, 255, .6) 55px 65px,
              rgba(255, 255, 255, 0) -18px 120px,
              rgba(255, 255, 255, 0) 12px 120px,
              rgba(255, 255, 255, .6) -31px 70px,
              rgba(255, 255, 255, .6) 30px 60px;
          }
          26% {
            box-shadow:
              rgba(255, 255, 255, .6) -10px 45px,
              rgba(255, 255, 255, .6) 40px 60px,
              rgba(255, 255, 255, .6) -50px 90px,
              rgba(255, 255, 255, .6) 55px 65px,
              rgba(255, 255, 255, 0) -18px 40px,
              rgba(255, 255, 255, 0) 12px 20px,
              rgba(255, 255, 255, .6) -31px 70px,
              rgba(255, 255, 255, .6) 30px 60px;
          }
          50% {
            box-shadow:
              rgba(255, 255, 255, .6) -10px 70px,
              rgba(255, 255, 255, .6) 40px 80px,
              rgba(255, 255, 255, 0) -50px 100px,
              rgba(255, 255, 255, .6) 55px 80px,
              rgba(255, 255, 255, .6) -18px 60px,
              rgba(255, 255, 255, .6) 12px 45px,
              rgba(255, 255, 255, .6) -31px 95px,
              rgba(255, 255, 255, .6) 30px 85px;
          }
          51% {
            box-shadow:
              rgba(255, 255, 255, .6) -10px 70px,
              rgba(255, 255, 255, .6) 40px 80px,
              rgba(255, 255, 255, 0) -50px 45px,
              rgba(255, 255, 255, .6) 55px 80px,
              rgba(255, 255, 255, .6) -18px 60px,
              rgba(255, 255, 255, .6) 12px 45px,
              rgba(255, 255, 255, .6) -31px 95px,
              rgba(255, 255, 255, .6) 30px 85px;
          }
          75% {
            box-shadow:
              rgba(255, 255, 255, .6) -10px 95px,
              rgba(255, 255, 255, .6) 40px 100px,
              rgba(255, 255, 255, .6) -50px 60px,
              rgba(255, 255, 255, 0) 55px 95px,
              rgba(255, 255, 255, .6) -18px 80px,
              rgba(255, 255, 255, .6) 12px 70px,
              rgba(255, 255, 255, 0) -31px 120px,
              rgba(255, 255, 255, 0) 30px 110px;
          }
          76% {
            box-shadow:
              rgba(255, 255, 255, .6) -10px 95px,
              rgba(255, 255, 255, .6) 40px 100px,
              rgba(255, 255, 255, .6) -50px 60px,
              rgba(255, 255, 255, 0) 55px 35px,
              rgba(255, 255, 255, .6) -18px 80px,
              rgba(255, 255, 255, .6) 12px 70px,
              rgba(255, 255, 255, 0) -31px 25px,
              rgba(255, 255, 255, 0) 30px 15px;
          }
          100% {
            box-shadow:
              rgba(255, 255, 255, 0) -10px 120px,
              rgba(255, 255, 255, 0) 40px 120px,
              rgba(255, 255, 255, .6) -50px 75px,
              rgba(255, 255, 255, .6) 55px 50px,
              rgba(255, 255, 255, .6) -18px 100px,
              rgba(255, 255, 255, .6) 12px 95px,
              rgba(255, 255, 255, .6) -31px 45px,
              rgba(255, 255, 255, .6) 30px 35px;
          }
        }
      </style>

chrome 浏览器图标

图片描述

<div class="css-live-wrap">
        <summary>Chrome(单标签实现)</summary>
        <summary>利用渐变实现</summary>
        <hgroup class="Chrome">
          <h1></h1>
        </hgroup>
      </div>
      <style>
        .Chrome{
          position: absolute;
          top: 50%;left: 50%;
          width: 180px;height: 180px;
          transform: translate(-50%, -50%);
          box-shadow:0 0px 4px #999,0 0 2px #ddd inset;
          border-radius:50%;
          background-image:
            radial-gradient(#4FACF5 0%,#2196F3 28%, transparent 28%),
            radial-gradient(#fff 33%, transparent 33%),
            linear-gradient(-50deg,#FFEB3B 34%, transparent 34%),
            linear-gradient(60deg,#4CAF50 33%, transparent 33%),
            linear-gradient(180deg,#FF756B 0%, #F44336 30%, transparent 30%),
            linear-gradient(-120deg,#FFEB3B 40%, transparent 40%),
            linear-gradient(-60deg,#FFEB3B 30%, transparent 30%),
            linear-gradient(0deg,#4CAF50 45%, transparent 45%),
            linear-gradient(60deg,#4CAF50 30%, transparent 30%),
            linear-gradient(120deg,#F44336 50%, transparent 50%),
            linear-gradient(180deg,#F44336 30%, transparent 30%);
          background-position:0 0;
        }
      </style>

IE 浏览器图标

图片描述

<div class="css-live-wrap">
        <summary>IE(单标签实现)</summary>
        <summary>利用渐变、多重阴影实现</summary>
        <hgroup class="IE">
          <h1></h1>
        </hgroup>
      </div>
      <style>
        .IE{
          position: absolute;
          top: 50%;left: 50%;
          width:200px; height:200px;
          transform: translate(-50%, -50%);
          border-radius:50%;
          /* box-shadow:1px 2px 1px #ddd; */
          background-image:
             radial-gradient(#fff 38%, transparent 38%),
             radial-gradient(#09C 0%, #09C 100%);

        }
        .IE:before{
          content: "";
          width:285px;
          height:122px;
          background:none;
          border-radius:100%;
          position:absolute;
          top:33px;
          left:-45px;
          margin:auto;
          box-shadow:
            inset 0 12px 0 13px  #09c,
            -35px -8px 0 -5px #fff;
          transform: rotate(-35deg);
        }
        .IE:after {
          content: "";
          width: 120px;
          height: 25px;
          background: #09c;
          position: absolute;
          top: 80px;
          left: 0;
          right: 0;
          margin: auto;
          box-shadow: 50px 23px 0 -2px #fff
        }
      </style>

safari 浏览器图标

图片描述

<div class="css-live-wrap">
        <summary>safari(单标签实现)</summary>
        <summary>利用渐变、border、旋转实现</summary>
        <hgroup class="safari">
          <h1></h1>
        </hgroup>
      </div>
      <style>
        .safari{
          position: absolute;
          top: 50%;left: 50%;
          width:200px; height:200px;
          transform: translate(-50%, -50%);
          border-radius:50%;
          border:5px solid #E8E8E8;
          box-shadow:
            -1px 3px 1px 2px #999,
            -1px 3px 1px 2px #999 inset;
          background-image:
            radial-gradient(transparent 30%,#fff 30%,#fff 34%, transparent 34%),
            linear-gradient(rgba(255,255,255,.8) 100%, transparent 100%),
            linear-gradient(rgba(255,255,255,.8) 100%, transparent 100%),
            linear-gradient(rgba(255,255,255,.8) 100%, transparent 100%),
            linear-gradient(rgba(255,255,255,.8) 100%, transparent 100%),
            linear-gradient(#1DE3FF 0%, #1F52EF 100%);
          background-size: 50% 50%,20px 2px,20px 2px,2px 20px,2px 20px,100%,100%;
          background-repeat:no-repeat;
          background-position:center center, 175px center,5px center,center 175px,center 5px,0 0;

        }
        .safari::before{
          content:"";
          position: absolute;
          top: 10px;left: 50%;
          border-radius:10px;
          /* width:20px; height:80px; */
          border-bottom:100px solid rgba(255,255,255,.9);
          border-left:10px solid transparent;
          border-right:10px solid transparent;
          transform-origin:center 90px;
          z-index:-1;
          transform:translate(-50%, 0%) rotate(40deg);
        }
        .safari::after{
          content:"";
          position: absolute;
          top: 10px;left: 50%;
          border-radius:10px;
          /* width:20px; height:80px; */
          border-bottom:100px solid rgba(255,0,0,.9);
          border-left:10px solid transparent;
          border-right:10px solid transparent;
          transform-origin:center 90px;
          transform:translate(-50%, 0%) rotate(220deg);
        }
        .safari:hover::before{
            transition:transform 1s;
            transform:translate(-50%, 0%) rotate(70deg);
        }
        .safari:hover::after{
            transition:transform 1s;
            transform:translate(-50%, 0%) rotate(250deg);
        }
      </style>
查看原文

赞 54 收藏 40 评论 3

xiayefeng 收藏了文章 · 2018-12-19

理解跨域及常用解决方案

跨域,相信大家无论是在工作中还是在面试中经常遇到这个问题,常常在网上看到别人所整理的一些方法,看似知道是怎么回事,但如果没有动手实践过,总觉得自己没有真正的掌握,在这里,通过自己认真思考整理一些常用的方法。

跨域的产生

不用多讲,作为一名前端开发人员,相信大家都知道跨域是因为浏览器的同源策略所导致的。所谓同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个ip地址,也非同源。浏览器引入同源策略主要是为了防止XSS,CSRF攻击。

CSRF(Cross-site request forgery),跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSRF/XSRF。

在同源策略影响下,域名A向域名B发送Ajax请求,或操作Cookie、LocalStorage、indexDB等数据,或操作dom,js就会受到限制,但请求css,js等静态资源不受限制

clipboard.png

跨域的解决方案

1 通过jsonp跨域

首先说一下jsonp的原理,例如我们平时写html的时候常常会使用
<script data-original="www.b.com/js/jquery.js"></script>这种方式去取放在另外服务器上的静态资源,这个是不受同源策略所限制的,所以我们利用这一点可以解决跨域的问题。

主要代码如下:

1.1原生实现

在www.a.com域名写下如下代码,去请求www.b.com域名的数据
<script>
    var script = document.creatElement('script');
    script.type = 'text/javascript';
    script.src = 'http://www.b.com/getdata?callback=demo';
    
    function demo(res){
      console.log(res);
    }
</script>

这里,我们利用动态脚本的src属性,变相地发送了一个http://www.b.com/getdata?call...。这时候,b.com页面接受到这个请求时,如果没有JSONP,会正常返回json的数据结果,像这样:{ msg: 'helloworld' },而利用JSONP,服务端会接受这个callback参数,然后用这个参数值包装要返回的数据:demo({msg: 'helloworld'});

这时候,如果a.com的页面上正好有一个demo 的函数:

function demo(res){

  console.log(res);

}

当远程数据一返回的时候,随着动态脚本的执行,这个demo函数就会被执行。

1.2 jquery ajax请求实现

$.ajax({
    url:'http://www.b.com/getdata',
    type:'get',
    dataType: 'jsonp',  // 请求方式为jsonp
    jsonpCallback: 'demo', // 自定义回调函数名
    data: {}
});

服务端代码实现:

以nodejs为例

var http = require(http);
//引入url模块解析url字符串
var url = require('url);
//引入querystring模块处理query字符串
var querystring = require('querystring');

var server = http.createServer();

server.on('request',function(req,res){
    var urlPath = url.parse(req.url).pathname;
    var param = querystring .parse(req.url.split('?')[1]);
    
    if(urlPath === '/getData' && param.callback) {
    
        res.writeHead(200,{'Content-Type','application/json;charset=utf-8'});
        
        var data = { msg: 'helloworld' };
        data = JSON.stringify(data );
        
        var callback = param .callback+'('+data+');';
        res.write(callback);
        
        res.end();
    } else {
        res.writeHead(200, {'Content-Type':'text/html;charset=utf-8'});
        
        res.write('Hell World\n');
        res.end();    
    
    }


})
jsonp缺点:只能使用get请求,不推荐使用

2 CORS 跨域资源共享

跨域资源共享(CORS) 是一种机制,它使用额外的 HTTP 头来告诉浏览器 让运行在一个 origin (domain) 上的Web应用被准许访问来自不同源服务器上的指定的资源。当一个资源从与该资源本身所在的服务器不同的域或端口请求一个资源时,资源会发起一个跨域 HTTP 请求。

Cross-Origin Resource Sharing跨域资源共享,应该算是现在比较推荐的跨域处理方案.不仅适用于各种Method,而且更加方便和简单
目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。

2.1 简单请求和非简单请求

浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。

简单请求同时满足以下条件,只要不满足以下条件的则为非简单请求

clipboard.png

非简单请求会发出一次预检测请求,返回码是204,预检测通过才会真正发出请求,这才返回200。这里通过前端发请求的时候增加一个额外的headers来触发非简单请求。

2.2 进行带有身份凭证的CORS 请求

  • 默认情况下的跨域请求都是不会把cookie发送给服务器的,在需要发送的情况下,如果是xhr,那么需要设置xhr.withCredentials=true,
  • 如果是采用fetch获取的话,那么需要在request里面设置 credentials:'include',
  • 但是如果服务器在预请求的时候没返回Access-Control-Allow-Crenditials:true的话,那么在实际请求的时候,cookie是不会被发送给服务器端的,要特别注意对于简单的get请求,不会有预请求的过程,
  • 那么在实际请求的时候,如果服务器没有返回Access-Control-Allow-Crenditials:true的话那么响应结果浏览器也不会交给请求者
对于附带身份凭证的请求,服务器不得设置 Access-Control-Allow-Origin 的值为“*”。

这是因为请求的首部中携带了 Cookie 信息,如果 Access-Control-Allow-Origin
的值为“*”,请求将会失败。而将 Access-Control-Allow-Origin 的值设置为
http://www.a.com,则请求将成功执行。

2.3 HTTP 响应首部字段

  • Access-Control-Allow-Origin: <origin> | *
  • Access-Control-Expose-Headers 头让服务器把允许浏览器访问的头放入白名单
  • Access-Control-Max-Age 头指定了preflight请求的结果能够被缓存多久
  • Access-Control-Allow-Credentials
    头指定了当浏览器的credentials设置为true时是否允许浏览器读取response的内容。
  • Access-Control-Allow-Methods 首部字段用于预检请求的响应。其指明了实际请求所允许使用的 HTTP 方法。
  • Access-Control-Allow-Headers 首部字段用于预检请求的响应。其指明了实际请求中允许携带的首部字段。

2.4 以nodejs express为例,说明如何使用cors解决跨域

var express=require('express');
var url=require('url');
var app=express();
var allowCrossDomain = function(req, res, next) {
    res.header('Access-Control-Allow-Origin', 'http://localhost:63342');
    res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
    res.header('Access-Control-Allow-Headers', 'Content-Type');
    res.header('Access-Control-Allow-Credentials','true');
    next();
};
app.use(allowCrossDomain);
app.get('/getData',function (req,res,next) {
    var queryValue=url.parse(req.url).query;
    if(queryValue==='fortunewheel@sina.com'){
        res.send(true);
    }else {
        res.send(false);
    }

});
app.listen(3001);
实际开发过程中,为了安全,会和token一起使用

3 window.postMessage

postMessage是HTML5 XMLHttpRequest Level 2中的API,且是为数不多可以跨域操作的window属性之一,它可用于解决以下方面的问题:

  • iframe嵌套页面跨域通信
  • 页面和其打开的新窗口的通信
  • 多窗口之间消息传递

用法:
postMessage(data,origin)方法接受两个参数,

data:需要传递的数据,html5规范支持任意基本类型或可复制的对象,但部分浏览器只支持字符串,所以传参时最好用JSON.stringify()序列化。
origin:协议+主机+端口号,也可以设置为"*",表示可以传递给任意窗口,如果要指定和当前窗口同源的话设置为"/"。

代码示例:
http://www.a.com/a.html

<iframe id="iframe" data-original="http://www.b.com/b.html" style="display:none;"></iframe>
<script>       
    var iframe = document.getElementById('iframe');
    iframe.onload = function() {
        var data = {
            name: 'jianjian'
        };
        // 向http://www.b.com传送跨域数据
        iframe.contentWindow.postMessage(JSON.stringify(data),'http://www.b.com');
    };

    // 接受http://www.b.com返回数据
    window.addEventListener('message', function(e) {
        alert('data from http://www.b.com---> ' + e.data);
    }, false);
</script>

http://www.b.com/b.html

<script>
    // 接收http://www.a.com/a.html的数据
    window.addEventListener('message', function(e) {
        alert('data from http://www.a.com/a.html---> ' + e.data);

        var data = JSON.parse(e.data);
        if (data) {
            data.number = 16;

            // 处理后再发回http://www.a.com/a.html
            window.parent.postMessage(JSON.stringify(data), 'http://www.a.com');
        }
    }, false);
</script>

4 document.domain

这种方式只适合主域名相同,但子域名不同的iframe跨域。
实现原理:两个页面都通过js强制设置document.domain为基础主域,就实现了同域。

使用方式:
http://www.a.com/a.html

<iframe id="iframe" data-original="http://www.child.a.com/b.html" style="display:none;"></iframe>
<script>      
      document.domain = 'a.com'; 
      
      var a = 'hello world';
   
 
</script>

"http://www.child.a.com/b.html

<script>      
      document.domain = 'a.com'; 
      
      var b = window.parent.a;
   
      console.log(b);
</script>

5 window.name

window.name 传输技术的基本原理:
当在浏览器中打开一个页面,或者在页面中添加一个iframe时即会创建一个对应的window对象,当页面加载另一个新的页面时,window.name的属性是不会变的。这样就可以利用在页面动态添加一个iframe然后加载数据页面,在数据页面将需要的数据赋值给window.name。然而此时承载的iframe的parent页面还是不能直接访问不在同一域下的iframe的那么属性,这时,只需要将iframe再加载一个与承载页面同域的空白页面,即可对window.name进行数据读取。
通过iframe的src属性由外域转向本地域,跨域数据即由iframe的window.name从外域传递到本地域。这个就巧妙地绕过了浏览器的跨域访问限制,但同时它又是安全操作。

具体实现:
http://www.a.com/a.html 主页面
http://www.b.com/b.html 数据页面
http://www.a.com/proxy.html 代理页面

http://www.a.com/a.html代码

<script>

 
function crosDomainGetData(url,callback){

    var state = 0;
    var iframe = document.createElement('iframe);
    iframe.src = url;
    
    iframe.onload = function(){
        if(state === 1){
           //代理页面成功过后,读取window.name
            var data = iframe.contentWindow.name;
            callback&&callback(data);
            
            //销毁iframe
            iframe.contentWindow.document.write('');
            iframe.contentWindow.close();
            document.body.removeChild(iframe);          
        } else {
            //第一次加载数据页面成功后,切换代理页面
            state = 1;
            iframe.contentWindow.location = 'http://www.a.com/proxy.html';
        }
    }
    
    document.body.appendChild(iframe);


}

crosDomainGetData('http://www.b.com/b.html',function(data){
    alert(data);
})




</script>

http://www.b.com/b.html代码

    window.name = '123'

http://www.a.com/proxy.html空白

6 nginx代理跨域

server{
    # 监听8080端口
    listen 8080;
    # 域名是localhost
    server_name localhost;
    #凡是localhost:8080/api这个样子的,都转发到真正的服务端地址http://www.b.com:8080 
    location ^~ /api {
        proxy_pass http://www.b.com:8080;
    }    
}
配置之后就不需要前端做什么修改了,一般我们在前后端分离项目中开发阶段会采用这种方式,但不是所有场景都能这样做,例如后端接口是一个公共的API,比如一些公共服务获取天气什么的。

7 WebSocket协议跨域

websoket协议天然支持跨域,你只需要学会如何使用它即可,关于websocket协议请看我的另外一篇文章WebSocket网络通信协议

参考文章:
https://developer.mozilla.org...
https://segmentfault.com/a/11...

查看原文

认证与成就

  • 获得 0 次点赞
  • 获得 1 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 1 枚铜徽章

擅长技能
编辑

(゚∀゚ )
暂时没有

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2018-11-23
个人主页被 112 人浏览