首页

前端性能优化

seo达人

如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里

减少页面加载时间的方法:
1.压缩html css js等文件
2.减少DNS解析
3.减少DOM元素,对于相关操作可以缓存节点
4.改变页面元素样式时尽量操作className,而不是频繁使用xxx.style.(其实还有很多这种DOM修改操作的细节比如opacity替代visibility ,多个DOM统一操作(虽然V8会有缓存优化) ,先将DOM离线,即display:none;修改后显示 ,不要把DOM放在已给循环中作为循环变量 ,不要使用table )
5.避免不必要的重定向
6.合并js css及图片,减少请求数量
7.使用CDN
8.合理利用缓存
9…

关于CDN?
https://zhuanlan.zhihu.com/p/39028766

CDN中文名“内容分发网路”,作用是减少传播时延,找最近的节点。CDN本质就一个是缓存,只是这个缓存离你特别近,不管是作为用户还是开发人员都能从中享受一点福利。
CDN的优点
1.访问加速
2.减轻服务器负载
3.抗住攻击
CDN的缺点
1.只能对静态资源加速
2.内容更新时需要分发到其他节点
3.代价高昂
与传统网站访问方式的不同
传统的网站会直接解析域名获得IP地址然后发送请求
使用了CDN的网站则增加了缓存层,解析域名→获取对应CNAME域名→对获取的域名进行解析得到缓存服务器的IP地址,将用户的访问请求引导到最优的缓存节点而不是源站。

总结:通过权威DNS服务器来实现最优节点的选择,通过缓存来减少源站的压力。主要应用于静态网页、大文件的下载等等。
应用与踩坑

缓存设置
max-age在Cache-Control中经常用于缓存的控制,可是max-age设置的缓存会应用于一个请求经过的每一级,如果我们希望CDN不缓存那么久,要怎么办呢?那就是s-maxage,它用于设置代理服务器的缓存时间,会覆盖max-age的设置,这样我们可以把max-age用于本地缓存,把s-maxage用于CDN缓存时间,避免脏数据的产生。

缓存命中率
对于一个缓存而言,还有一点非常重要,就是你的缓存到底有没有用,衡量这个东西的就是缓存命中率。如果只是静态资源,在刷新缓存之后,可能导致命中率下降,因此CDN的资源不适合经常刷新,换句话说,如果一个请求结果会经常进行变更,那么CDN基本就没有什么存在的意义了。

判断是否命中缓存
还要考虑的一件事是:这个资源是否命中了CDN,是否是因为CDN导致的问题。

在各大厂商的CDN返回的数据中都会有一个X-Cache,上面内容是Hit或者Miss,还会加上诸如Memory或者Disk的缩写表示内存还是磁盘,如果出现Upstream或者Miss则表示没有命中。

资源预热
缓存设计中,预热是很重要的环节,在最初刚开始启动CDN的时候,CDN上并没有缓存数据,此刻大量的请求全部打向源站,肯定会把源站打挂,预热就是实现缓存热门数据,这样在业务上线时,CDN上已经有所需的数据了

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

vue父子组件互相传值

seo达人

如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里

一、父组件给子组件传值

1、父组件调用子组件的时候,绑定动态属性

/*传值可以是值“title”、是方法“run”、是组件“this”*/
<v-header :title="title" :run="run" :home="this"></v-header>
1
2
2、在子组件里面通过props接受父组件传过来的数据

<script>
    export default{
props:['title','run','home']
}
</script>

二、父组件主动获取子组件的数据和方法

1、调用子组件的时候定义一个ref

<v-header ref="header"></v-header>
1
2、在父组件里面通过以下方式获取属性和方法

this.$refs.header.属性
this.$refs.header.方法

三、子组件主动获取父组件的数据和方法

this.$parent.数据
this.$parent.方法

四、非父子组建传值

1、新建一个js文件 然后引入vue 实例化vue最后暴露这个实例

VueEvent.js

import Vue from 'vue';
var VueEvent = new Vue();
export default VueEvent;

2、在要广播的地方引入刚才定义的实例,并进行广播

home.vue

<script>
import VueEvent from './VueEvent.js';
    export default{
        methods:{
            emitNews(){
                /*广播数据*/
                VueEvent.$emit('to-news',this.数据)
            }
        }
}
</script>

3、在要接收数据的地方接受广播

news.vue

<script>
import VueEvent from './VueEvent.js';
    export default{
        /*在生命周期函数里写,编译的时候就调用*/
        mounted(){
            /*接受广播*/
            VueEvent.$on('to-news',function(data){
                console.log(data);
            })
        }
}
</script>


Bitmap三级缓存 和二次采样

seo达人

如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里

一.为什么Bitmap三级缓存?
没有缓存的弊端 :费流量, 加载速度慢
加入缓存的优点: 省流量,支持离线浏览
二.原理

从内存获取图片, 如果存在, 则显示; 如果不存在, 则从SD卡中获取图片
从SD卡中获取图片, 如果文件中存在, 显示, 并且添加到内存中; 否则开启网络下载图片
从网络下载图片, 如果下载成功, 则添加到缓存中, 存入SD卡, 显示图片
三.代码
(1)添加读写SD卡的权限和网络权限



// //Lrucache存储工具类
public class LruUtils {
private LruCache<String,Bitmap> lruCache;
private long max=Runtime.getRuntime().maxMemory();
public LruUtils(){
lruCache=new LruCache<String,Bitmap>((int)max/8){

        @Override
        protected int sizeOf(String key, Bitmap value) {
            return value.getByteCount();
        }
    };
}
public Bitmap getBitmap(String key){
    return lruCache.get(key);
}
public void setBitmap(String key,Bitmap bitmap){
    lruCache.put(key,bitmap);
}
1
2
3
4
5
6
7
8
9
10
11
12
}
//SD卡工具类
public class SDUtils {

public static void setBitmap(String name, Bitmap bitmap) {

    if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
        File file = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
        File file1 = new File(file, name);

        try {

            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, new FileOutputStream(file1));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }
}
public static Bitmap getBitmap(String name){

    if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
        File file=Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
        File file1=new File(file,name);

        return BitmapFactory.decodeFile(file1.getAbsolutePath());

    }
    return null;
}

}
//网络

import android.app.AlertDialog;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;

import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.ExecutionException;

public class NewUtils {
public static Bitmap getBitmap(String url) throws ExecutionException, InterruptedException {
return new MyTask().execute(url).get();
}
static class MyTask extends AsyncTask<String,Void,Bitmap>{
@Override
protected Bitmap doInBackground(String… strings) {
String imageUrl = strings[0];
HttpURLConnection conn = null;
try {
URL url = new URL(imageUrl);
conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(5000);
conn.setConnectTimeout(5000);
conn.setRequestMethod(“GET”);
if (conn.getResponseCode() == 200) {
InputStream is = conn.getInputStream();
Bitmap bitmap = BitmapFactory.decodeStream(is);
return bitmap;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (conn != null) {
conn.disconnect();
}
}
return null;
}
}

}
//使用三个工具类完成Bitmap的三级缓存
package com.example.administrator.myapplication;

import android.graphics.Bitmap;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;

import java.util.concurrent.ExecutionException;

public class MainActivity extends AppCompatActivity {
private ImageView imageView;
Button button;
private LruUtils lruUtils= new LruUtils();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button=findViewById(R.id.button);
imageView=findViewById(R.id.imageview);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Bitmap bitmap=lruUtils.getBitmap(“czn”);
if (bitmap!=null){
imageView.setImageBitmap(bitmap);
Toast.makeText(MainActivity.this, “图片存内存”, Toast.LENGTH_SHORT).show();
}else{
bitmap=SDUtils.getBitmap(“czn.jpg”);
if (bitmap!=null){
imageView.setImageBitmap(bitmap);
Toast.makeText(MainActivity.this, “图片存SD卡”, Toast.LENGTH_SHORT).show();
lruUtils.setBitmap(“czn”,bitmap);
}else{
try {
bitmap=NewUtils.getBitmap(“http://pic1.win4000.com/wallpaper/e/50d80458e1373.jpg”);
if (bitmap!=null){
imageView.setImageBitmap(bitmap);
Toast.makeText(MainActivity.this, “图片存网络”, Toast.LENGTH_SHORT).show();
SDUtils.setBitmap(“czn.jpg”,bitmap);
lruUtils.setBitmap(“czn”,bitmap);
}else{
Toast.makeText(MainActivity.this, “没有找到”, Toast.LENGTH_SHORT).show();
}

                    } catch (ExecutionException e) {
                        e.printStackTrace();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                }
            }
        }
    });
}

}
Bitmap二次采样


import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.concurrent.ExecutionException;

public class Main2Activity extends AppCompatActivity {
Button bt;
ImageView imageView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
bt=findViewById(R.id.bt);
imageView=findViewById(R.id.mimage);
bt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
Bitmap bitmap = new MyTask().execute(“https://cdn.duitang.com/uploads/item/201211/24/20121124230042_Bfhim.jpeg”).get();
imageView.setImageBitmap(bitmap);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}

        }

    });
}
class MyTask extends AsyncTask<String,Object,Bitmap>{

    @Override
    protected Bitmap doInBackground(String... strings) {
        try {
            URL url = new URL(strings[0]);
            HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
            if(urlConnection.getResponseCode()==200){
                InputStream inputStream = urlConnection.getInputStream();
                //将inputStream流存储起来
                ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                byte[] bytes = new byte[1024];
                int len=0;
                while((len=inputStream.read(bytes))!=-1){
                    byteArrayOutputStream.write(bytes,0,len);
                }
                //桶:网络的图片都放在数组里面了
                byte[] data = byteArrayOutputStream.toByteArray();
                //TODO 1:第一次采样:只采边框 计算压缩比例
                BitmapFactory.Options options = new BitmapFactory.Options();
                options.inJustDecodeBounds=true;//设置只采边框
                BitmapFactory.decodeByteArray(data,0,data.length,options);//采样
                int outWidth = options.outWidth;//获得原图的宽
                int outHeight = options.outHeight;//获得原图的高
                //计算缩放比例
                int size=1;
                while(outWidth/size>100||outHeight/size>100){
                    size*=2;
                }
                //TODO 2:第二次采样:按照比例才像素
                options.inJustDecodeBounds=false;//设置只采边框为fasle
                options.inSampleSize=size;//设置缩放比例
                Bitmap bitmap= BitmapFactory.decodeByteArray(data,0,data.length,options);//采样
                return  bitmap;
            }

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

Fragment的创建及使用

seo达人

如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里

Fragment,在Android中指的是碎片,也就是在不切换Activity时进行页面的切换,这是Android中的一个重点的内容,很多的应用程序中都有这样的功能,所以,接下来让我们具体的学习一下Fragment的使用
首先,要将一个Fragment给创建出来。
新建一个类,让这个类继承Fragment,并重写onCreatView()方法,之后,使用onCreatView中的inflater将一个布局文件转换为视图,并返回这个视图


之后在MainActivity中获得一个Fragment的管理者

之后我们通过这个管理者的beginTransaction()的方法获取事务管理者

然后,我们将之前写好的Fragment类给进行实例化

之后,我们使用事务管理者的replace()方法来给我们需要的控件上将我们的Fragment给显示出来

之后,使用事务管理者提交事务,这样我们的Fragment就完美的显示出来了

碎片可以在不影响Activity时进行一个页面的切换,所以,我们需要把我们需要显示的所有的Fragment都放到FrameLayout布局上
这样就可以实现Fragment的切换了
同时,在进行Fragment可以给Fragment添加一个回退栈的功能,使得每次按返回键是返回的上一个Fragment,而不是直接退出整个程序了


下面我们讲一下Fragment的生命周期
Fragment的生命周期分为11个部分,分别为:
onAttach()
onCreatView()
onCreat()
onActivityCreated()
onStart()
onResume()
onPause()
onStop()
onDestoryView()
onDestory()
onDetach()
Fragment的生命周期和Activity的生命周期一样重要,都是面试时的重点,一定要背下来

其次还要学习关于Fragment的传值
Fragment的传值分为两种方法,分别是Handler传值和接口回调方法,接下来就来学习一下两种不同的传值方式
首先是Handler的传值 ,
第一步是在我们需要拿到值的Fragment中建立一个静态的Handler,之后重写handleMessage()方法

第二步,在传值的Fragment的调用Handler进行传值

这样就可以实现了Fragment的Handler之间的传值

然后我们接着说关于接口回调的传值
首先定义一个外部接口

之后呢,在传值的Fragment里传递数据

然后,我们在接收的Fragment里实现接口并重写方法即可传递数据

这就是接口回调传递数据的方法
好了,关于Fragment的使用简单说到这里
--------------------- 
蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、网站建设 平面设计服务

for循环包裹setTimeout计时器

seo达人

如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里

for(var i = 0; i < 5; i++) {
console.log(i)
}

这样for循环可打印出 0 - 4的结果

for(var i = 0; i < 5; i++) {
    setTimeout(function() {
console.log(i)
    }, i * 1000)
}

但这样只能间隔一秒打印出5个5

原因在于 setTimeout是异步,等for循环全部完成 i 后才会执行

解决方法可以将 for循环中的var 变成 let

let只作用于for循环内,这样每次付给setTimeout的值都是当前值

或者在setTimeout外再包一层function

for(var i = 0; i < 5; i++) {
    (function(i) {setTimeout(function() {
console.log(i)
    }, i * 1000)})(i)

}

将 i 作为参数传到setTimeout中运行 这样就可以得到每隔1秒加1的log结果了
蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、网站建设 平面设计服务

WEB前端之HTML 规范

seo达人

如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里


摘要
优秀的项目源码,即使是多人开发,看代码也如出一人之手。统一的编码规范,可使代码更易于阅读,易于理解,易于维护

HTML 规范
缩进
统一两个空格缩进

命名规范
class 应以功能或内容命名,不以表现形式命名;
class 与 id 单词字母小写,多个单词组成时,采用中划线-分隔;
使用唯一的 id 作为 Javascript hook, 同时避免创建无样式信息的 class;
DOCTYPE 声明
HTML 文件必须加上 DOCTYPE 声明,并统一使用 HTML5 的文档声明:

<!DOCTYPE html>


meta 标签
统一使用 “UTF-8” 编码
<meta charset="utf-8">


SEO 优化
<!-- 页面关键词 -->
<meta name ="keywords" content =""/>
<!-- 页面描述 -->
<meta name ="description" content ="">
<!-- 网页作者 -->
<meta name ="author" content ="">


优先使用 IE 版本和 Chrome
<meta http-equiv ="X-UA-Compatible" content ="IE = edge,chrome = 1">


为移动设备添加视口
<!-- device-width 是指这个设备最理想的 viewport 宽度 -->
<!-- initial-scale=1.0 是指初始化的时候缩放大小是1,也就是不缩放 -->
<!-- user-scalable=0 是指禁止用户进行缩放 -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">


禁止自动识别页面中有可能是电话格式的数字
<meta name="format-detection" content="telephone=no">


团队约定:

pc 端:

<meta charset="utf-8">
<meta name="keywords" content="your keywords">
<meta name="description" content="your description">
<meta name="author" content="author,email address">
<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
web前端开发资源Q-q-u-n: 767273102 ,内有免费开发工具,零基础,进阶视频教程,希望新手少走弯路 

移动端:

<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<meta name="format-detection" content="telephone=no">


标签
html 标签分为以下几类:

自闭合标签(self-closing),无需闭合。例如:area、base、br、col、command、embed、hr、img、input、keygen、link、meta、param、source、track、wbr 等 )
闭合标签(closing tag),需闭合 。例如:textarea、title、h、div、span 等
团队约定:

所有具有开始标签和结束标签的元素都必须要写上起止标签,某些允许省略开始标签或和束标签的元素亦都要写上
自闭合标签不要加上结束标签
自定义标签的名字必须包含一个破折号(-),<x-tags>、<my-element>和<my-awesome-app>都是正确的名字,而<tabs>和<foo_bar>是不正确的。这样的限制使得 HTML 解析器可以分辨那些是标准元素,哪些是自定义元素
自定义标签必须写上开始标签和闭合标签
尽量减少标签数量
元素属性
元素属性值使用双引号语法
推荐:

<input type="text">


不推荐:

<input type=text>
<input type='text'>


代码嵌套
块元素可以包含内联元素或某些块元素,但内联元素却不能包含块元素,它只能包含其它的内联元素
标题和段落中不能包含块,如:h1、h2、h3、h4、h5、h6、p、dt
块与内联不能并列,块级元素与块级元素并列、内嵌元素与内嵌元素并列
有些标签是固定的嵌套规则,比如 ul 包含 li、ol 包含 li、dl 包含 dt 和 dd 等等。
灵活使用伪类
不要让非内容信息污染了你的 HTML,打乱了 HTML 结构。可以使用:before、:after 等伪类元素

推荐:

HTML 代码

<!-- That is clean markup! -->
<span class="text-box">
  See the square next to me?
</span>


CSS 代码:

/* We use a :before pseudo element to solve the design problem of placing a colored square in front of the text content */
.text-box:before {
  content: '';
  display: inline-block;
  width: 1rem;
  height: 1rem;
  background-color: red;
}

1

不推荐:

HTML 代码:

<!-- We should not introduce an additional element just to solve a design problem  -->
<span class="text-box">
  <span class="square"></span>
  See the square next to me?
</span>


CSS 代码:

.text-box > .square {
  display: inline-block;
  width: 1rem;
  height: 1rem;
  background-color: red;
}
web前端开发资源Q-q-u-n: 767273102 ,内有免费开发工具,零基础,进阶视频教程,希望新手少走弯路 

特殊符号必须使用转义符
符号 描述 转义符
空格 &nbsp;
< 小于 &lt;
> 大于 &gt;
& &amp;
" 引号 &quot;
纯数字输入框
使用 type=“tel” 而不是 type=“number”

<input type="tel">


类型属性
不需要为 CSS、JS 指定类型属性,HTML5 中默认已包含。

推荐:

<link rel="stylesheet" href="" >
<script src=""></script>


不推荐:

<link rel="stylesheet" type="text/css" href="" >
<script type="text/javascript" src="" ></script>


注释规范
单行注释

一般用于简单的描述,如某些状态描述、属性描述等
注释内容前后各一个空格字符,注释位于要注释代码的上面,单独占一行
推荐:

<!-- Comment Text -->
<div>...</div>


不推荐:

<div>...</div><!-- Comment Text -->

<div><!-- Comment Text -->
    ...
</div>


6
模块注释

注释内容前后各一个空格字符
<!-- S Comment Text -->表示模块开始
<!-- E Comment Text -->表示模块结束,模块与模块之间相隔一行
模块注释内部嵌套模块注释,<!-- /Comment Text -->
推荐:

<!-- S Comment Text A -->
<div class="mod_a">

    <div class="mod_b">
        ...
    </div>
    <!-- /mod_b -->

    <div class="mod_c">
    ...
    </div>
    <!-- /mod_c -->

</div>
<!-- E Comment Text A -->

<!-- S Comment Text D -->
<div class="mod_d">
    ...
</div>
<!-- E Comment Text D -->
web前端开发资源Q-q-u-n: 767273102 ,内有免费开发工具,零基础,进阶视频教程,希望新手少走弯路 

22
语义化
没有 CSS 的 HTML 是一个语义系统而不是 UI 系统
通常情况下,每个标签都是有语义的
语义化的 HTML 结构,有助于机器(搜索引擎)理解,另一方面多人协作时,能迅速了解开发者意图
建议页面中多使用语义化标签,而不是整个页面以 div 构成
常见标签语义:
标签 语义
<p> 段落
<hn> 标题(h1~h6)
<ul> 无序列表
<ol> 有序列表
<nav> 标记导航,仅对文档中重要的链接群使用
<main> 页面主要内容,一个页面只能使用一次。如果是 web 应用,则包围其主要功能
<article> 定义外部的内容,其中的内容独立于文档的其余部分
<section> 定义文档中的节(section、区段)。比如章节、页眉、页脚或文档中的其他部分。
<aside> 定义其所处内容之外的内容。如侧栏、文章的一组链接、广告、友情链接、相关产品列表
<header> 页眉通常包括网站标志、主导航、全站链接以及搜索框
<footer> 页脚,只有当父级是 body 时,才是整个页面的页脚
<figure> 规定独立的流内容(图像、图表、照片、代码等等)(默认有 40px 左右 margin)
蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、网站建设 平面设计服务

selenium处理网页下拉加载数据爬取并存入excel

seo达人

如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里

前言
之前有个同学询问我是否能够爬取知乎的全部回答,当初只会Scrapy无法实现下拉的数据全部加载。后来在意外中接触了selenium的自动化测试,看出了selenium的模拟能力的强大,而昨天有个同学问我能否爬取中国工商银行远程银行的精彩回答,我说可以试试。

思路
selenium模拟下拉直至底部
然后通过selenium获取数据集合
通过pandas写入excel
selenium模拟下拉直至底部
此处全靠一位大佬的博客点拨,实在不好意思的是,selenium就看了下常用的api,实在不懂如何判断是否加载完毕,而该博客代码的原理也好理解,通过不断下拉判断与上一次高度进行对比,知道前端页面的滚动高度属性就懂了,当然思想最重要。
见代码:

#将滚动条移动到页面的底部
all_window_height =  []  # 创建一个列表,用于记录每一次拖动滚动条后页面的最大高度
all_window_height.append(self.driver.execute_script("return document.body.scrollHeight;")) #当前页面的最大高度加入列表
while True:
self.driver.execute_script("scroll(0,100000)") # 执行拖动滚动条操作
time.sleep(3)
check_height = self.driver.execute_script("return document.body.scrollHeight;")
if check_height == all_window_height[-1]:  #判断拖动滚动条后的最大高度与上一次的最大高度的大小,相等表明到了最底部
print("我已下拉完毕")
break
else:
all_window_height.append(check_height) #如果不想等,将当前页面最大高度加入列表。
print("我正在下拉")

然后通过selenium获取数据集合
通过find_elements_by_css_selector方法获取元素对象列表,然后通过遍历列表获取单个对象,通过对象的text属性获取数据。
代码与"通过pandas写入excel"代码想结合。

通过pandas写入excel
example.xlsx

批量将数据依次写入excel,此处个人知道有两种写法,推荐后者。
写法一:

problem = cls.driver.find_elements_by_css_selector("li h2.item-title a")
data = pd.read_excel('example.xlsx', sheet_name = 'Sheet1')
problemtext = []
for i in problem:
problemtext .append(i.text)
replytext = []
reply = cls.driver.find_elements_by_css_selector("div.item-right p")
for j in reply:
    replytext.append(j.text)
    data.loc[row,'答案'] = j.text
data['问题'] = problemtext
data['答案'] = replytext

DataFrame(data).to_excel('test.xlsx', sheet_name='Sheet1')

写法二:

problem = cls.driver.find_elements_by_css_selector("li h2.item-title a")
data = pd.read_excel('example.xlsx', sheet_name = 'Sheet1')
row = 1
for i in problem:
    data.loc[row,'问题'] = i.text
    row += 1
row = 1
reply = cls.driver.find_elements_by_css_selector("div.item-right p")
for j in reply:
    data.loc[row,'答案'] = j.text
    row += 1

DataFrame(data).to_excel('test.xlsx', sheet_name='Sheet1')

完整代码
import pandas as pd
from pandas import DataFrame
import unittest
import time
from selenium import webdriver
from selenium.webdriver.support.ui import Select
from selenium.webdriver.support.select import Select
from selenium.webdriver.support.ui import WebDriverWait

class autoLogin(unittest.TestCase):

URL = 'http://zhidao.baidu.com/business/profile?id=87701'


@classmethod
def setUpClass(cls):
cls.driver = webdriver.Firefox()
cls.driver.implicitly_wait(20)
cls.driver.maximize_window()



def test_search_by_selenium(self):
self.driver.get(self.URL)
self.driver.title
time.sleep(1)
#将滚动条移动到页面的底部
all_window_height =  []
all_window_height.append(self.driver.execute_script("return document.body.scrollHeight;"))
while True:
self.driver.execute_script("scroll(0,100000)") 
time.sleep(3)
check_height = self.driver.execute_script("return document.body.scrollHeight;")
if check_height == all_window_height[-1]:  
print("我已下拉完毕")
break
else:
all_window_height.append(check_height) 
print("我正在下拉")

@classmethod
def tearDownClass(cls):
html=cls.driver.page_source
problem = cls.driver.find_elements_by_css_selector("li h2.item-title a")
data = pd.read_excel('example.xlsx', sheet_name = 'Sheet1')
row = 1
for i in problem:
    data.loc[row,'问题'] = i.text
    row += 1
row = 1
reply = cls.driver.find_elements_by_css_selector("div.item-right p")
for j in reply:
    data.loc[row,'答案'] = j.text
    row += 1
    
DataFrame(data).to_excel('test.xlsx', sheet_name='Sheet1')

#保存成网页
with open("index.html", "wb") as f:
f.write(html.encode())
f.close()
cls.driver.quit()

if __name__ == '__main__':
unittest.main(verbosity=2)

text.xlsx


总结
在使用Scrapy爬虫时,可以通过selenium来执行网页中的一些js脚本,但是如何将二者结合起来,以及各种框架之间的灵活运用,都将是我需要面对的。
--------------------- 
蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、网站建设 平面设计服务

移动端 验证码/密码 输入框实现--安卓/ios适用

seo达人

如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里

先贴图,需要实现的效果是这样的。



实现思路有两个:

 

1、用6个input,输入一个数字后将focus给下一个输入框。

2、用一个input和6个span,input隐藏,用span显示。

 

现在大部分都是使用的第二种方法。(当然,如果你能说服产品也可以只用一个普通的input输入框,就什么都不用考虑了)

 

两种方案遇到的坑,以及优缺点,如下:

 

方案一:6个input。

 

主要就是用js切换focus,在安卓是相当流畅的,但是在ios会严重卡顿,简直逼死强迫症。

 

HTML:

<div class="divYZM">
    <!-- onpropertychange是为了避免在ios中oninput方法不被触发 -->
    <input id="check_1" class="numDiv" type="number" oninput="inputNext(check_1)" onpropertychange="inputNext(check_1)"/>
    <input id="check_2" class="numDiv" type="number" oninput="inputNext(check_2)" onpropertychange="inputNext(check_2)"/>
    <input id="check_3" class="numDiv" type="number" oninput="inputNext(check_3)" onpropertychange="inputNext(check_3)"/>
    <input id="check_4" class="numDiv" type="number" oninput="inputNext(check_4)" onpropertychange="inputNext(check_4)"/>
    <input id="check_5" class="numDiv" type="number" oninput="inputNext(check_5)" onpropertychange="inputNext(check_5)"/>
    <input id="check_6" class="numDiv" type="number" oninput="inputNext(check_6)" onpropertychange="inputNext(check_6)"/>
</div>
JS:

function inputNext (id){ // 传过来的id是个对象
    var index = Number(id.id.split("_")[1])
    if (id.value.length < 1) { // 删除
        id.value = ''
        if (index > 1) {
            var preId = 'check_' + Number(Number(index) - 1)
            document.getElementById(preId).focus()
            return false
        }
    } else {
        if(id.value.length>1) {
            var nextValue = id.value.slice(1, 2)
            var nextId = 'check_' + Number(Number(index) + 1)
            id.value = id.value.slice(0, 1)
            if ((index+1) <= 6) {
                document.getElementById(nextId).value = nextValue
                document.getElementById(nextId).focus()
            }
        }
    }
}
PS:我这里写的删除方法是有问题的,这也是我果断放弃这种方案的原因之一。

 

如果正常输入,然后删除是可以的。

 

但是输入几个数后,先点击中间的框删除一个数字,再回到最后,便只能将中间到最后的这几个删掉,最前面的还需要手动点一下得到focus才能删除。

 

这对用户来说,简直太不友好了。。。

 

CSS:

.divYZM{
    width: 90%;
    margin: 0 auto;
    height: 100px;
    background-color: rgba(74, 35, 35, 0.42);
}
.numDiv{
    display: block;
    width: 10%;
    float: left;
    border-radius: 5px;
    text-align: center;
    line-height: 60px;
    font-size: 20px;
    font-weight: 900;
    color: red;
    background-color: white;
    height: 60px;
    border: 0;
    padding: 0;
    margin: 0;
    margin-left: 5.7%;
    top: 20px;
    position: relative;
    caret-color: transparent;
}
这里遇到的坑,举例一个。

 

input限制长度的属性maxlength

 

a、与如下两种配合使用(tel也可以限制)

<input type="text">  或者
<input type="password">
 

b、当type为number时不起作用。这时需要用js控制。

<input type="number" oninput="if(value.length>5) value=value.slice(0,5)" />
注意:此外,tel类型的input在ios上会调出全数字键盘,而number类型的input则会调出带有标点符号的键盘。

 

 

方案二:1个input和6个span。

 

隐藏input,用span显示内容。大坑就是,何种情况下能调起ios的软键盘呢?

 

先贴一下我刚开始的input样式。

width: 0;
height :0;
border: 0;
padding: 0;
margin: 0;

第二种
display:none;
 

简单粗暴,结果就是,ios木得反应。为啥呢,我想不通。

 

后来在晚上睡觉的时候我在想,我这两种方式input都么有占位啊,那是不是占位了就可以了呢?

 

经测果然是可以的(默默谴责自己懒了一下,没有将不隐藏input的情况,在手机上进行测试)。

 

接下来贴正确代码。

 

CSS:

#yzm{
    width: 0;
    border: 0;
    padding: 0;
    margin: 0;
    height: .44rem;
    position: absolute;
    outline: none;
    color: transparent;
    text-shadow: 0 0 0 transparent;
    width: 300%;
    margin-left: -100%;
}
#yzmTable {
    width: 90%;
    margin: 0 auto;
    height: 100px;
    /* border: 1px solid red; */
    background-color: rgba(74, 35, 35, 0.42);
    /* opacity: 0.1; */
}
#yzmTable span { 
    display: block;
    width: 10%;
    float: left;
    border-radius: 5px;
    text-align: center;
    line-height: 60px;
    font-size: 20px;
    font-weight: 900;
    color: red;
    background-color: white;
    height: 60px;
    margin-left: 5.7%;
    top: 20px;
    position: relative;
}
这里对input的样式也包括对光标的隐藏,我在第一种方案中对光标未进行处理,因为在看到ios的卡卡卡之后果断放弃了第一种方案。

 

HTML:

<input id="yzm" type="tel" maxlength="6" value="" oninput="yzmInsert()">
<div id="yzmTable">
    <span id="s_1" onclick="intoYzm(1)">&nbsp;&nbsp;</span>
    <span id="s_2" onclick="intoYzm(2)">&nbsp;&nbsp;</span>
    <span id="s_3" onclick="intoYzm(3)">&nbsp;&nbsp;</span>
    <span id="s_4" onclick="intoYzm(4)">&nbsp;&nbsp;</span>
    <span id="s_5" onclick="intoYzm(5)">&nbsp;&nbsp;</span>
    <span id="s_6" onclick="intoYzm(6)">&nbsp;&nbsp;</span>
</div>
JS:

function intoYzm(index) {
    var ele = document.getElementById("yzm")
    ele.focus()
}
 
function yzmInsert() { // input内容改变时触发
    for (var i = 1; i <= 6; i++) {
        var nextId = 's_' + i
        document.getElementById(nextId).innerHTML = '&nbsp;&nbsp;'
    }
    var yzm = document.getElementById("yzm").value
    var yzmArr = yzm.split('');
    for (var i = 0; i < yzmArr.length; i++) {
        const num = yzmArr[i];
        var id = 's_' + Number(i + 1)
        document.getElementById(id).innerHTML = '&nbsp;' + num + '&nbsp;'
    }
}
 
// 收起软键盘的方法,点击除了输入框之外的其他区域就收起软键盘
$('body').on('touchend', function(el) {
    if(el.target.tagName != 'SPAN') {
            $('yzm').blur()    
      }
})
 

在第二种方案中有两个地方注意下:

 

a、在js方法中加了对全局中6个span标签(即六个输入框)之外区域点击事件的监听,用以收起软键盘,方法如下。

$('body').on('touchend', function(el) {
    if(el.target.tagName != 'SPAN') {
        $('yzm').blur()
    }
})
 (比较粗糙,如果页面中还有别的部分就比较受影响了,可以自行改进)

b、在隐藏的input中添加了onclick方法,如下并且在其中用了blur方法使得此输入框失去焦点。为什么这么做呢?

<input id="yzm" type="tel" maxlength="6" value="" oninput="yzmInsert()" onclick="this.blur();">
因为此处的隐藏并非真正的隐藏,而是透明化处理,边框包括光标全部透明化,但实际上它还是占位的,所以当你点击输入框上方空白处时,仍会唤起软键盘,就和我们之前所想的点击输入框之外区域就收起软键盘冲突了。

 

因此将input自身的点击获取focus禁止掉,就OK了。

 

之前都是自己乱七八槽的瞎记,第一次写给别人看,经验不足,时间仓促。不足之处,还望指正。

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


软件测试 学习之路 CSS (四)

seo达人

如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里

一、文字样式中阶

字体样式
代码格式:
font: 文字粗细 大小/行高 字体名称;
例子:font: bold 200px/400px "微软雅黑";
2.字体阴影
代码格式:
text-shadow:x y r color;
注:x是为负数则阴影向左,整数向右,同理y正数向上,负数向下,r代表阴影模糊程度,数值月大则越模糊,其单位都是px,color为文字颜色。
例子:text-shadow: 10px 10px 0px red;
提示:允许一段文字有多层阴影,多层之间用逗号隔开,每一层内,不同参数用空格隔开。

凹凸字体 阴影巧用
原理:通过背景颜色以及不同于背景颜色的阴影打造凹凸字体效果
例子:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>凹凸文字</title>
<style type="text/css">
body{
background: #ccc;
}
div{
color: #ccc;
text-align: center;
font: bold 200px/400px "微软雅黑";
/*text-shadow: 1px 1px 0px #fff;-1px -1px 0px #333;*/
text-shadow: 1px 1px 0px #333,-1px -1px 0px #fff;
}
</style>
</head>
<body>
<div >
凹凸文字
</div>
</body>
</html>

二、过渡属性

过渡属性的作用就是体现元素默认样式与最终样式变化的过程。
代码格式:transition:all 1s linear 0s;
注:

第一个参数的作用是设置元素的哪些属性过渡,all表示全部过渡,width代表属性宽度过渡,其他不过渡,其他属性也一样。
的哥属性设置过渡需要的时长,单位s不能省略。
第三个属性设置过渡延迟多少秒执行,单位s不能省略。
hover 设置鼠标移到某一元素时状态。
transition 这个属性既可以添加在元素默认状态,也可以添加在鼠标上移状态即添加在hover标签内,区别就是第二种做法在鼠标离开时候不会发生过渡变化。
例子:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>过渡属性</title>
<style type="text/css">
div{
width: 200px;
height: 200px;
background-color: green;

transition:all 1s linear 0s;
 
}
div:hover{
width: 600px;
background-color: yellow;
}
</style>
</head>
<body>
<div id="\">

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

vue 移动端弹出键盘导致页面fixed布局错乱

seo达人

如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里

话不多说,直接上问题图片


这里确认按钮是fixed布局 bottom:0 弹出键盘之后按钮被顶到了键盘上面

网上搜到的解决方案有两种,

一种是监听页面高度(我采用的这种)

一种是监听软键盘事件(ios和安卓实现方式不同,未采用)

下面是实现代码

data() {
    return {
        docmHeight: document.documentElement.clientHeight ||document.body.clientHeight,
        showHeight: document.documentElement.clientHeight ||document.body.clientHeight,
        hideshow:true  //显示或者隐藏footer
    }
  },
watch: {
        //监听显示高度
      showHeight:function() {
          if(this.docmHeight > this.showHeight){
            //隐藏
              this.hideshow=false
          }else{
            //显示
              this.hideshow=true
          }
      }
  },
mounted() {
      //监听事件
      window.onresize = ()=>{
          return(()=>{
              this.showHeight = document.documentElement.clientHeight || document.body.clientHeight;
      })()
      }
 
  },
<div class="bottom" v-show="hideshow">
    <div class="btn">
      确认操作
    </div>
  </div>
我这里使用的是方法是:当键盘弹出时,将按钮隐藏。如果必须出现按钮的话,可以修改按钮回归到正常的流中。
蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、网站建设 平面设计服务

日历

链接

blogger

蓝蓝 http://www.lanlanwork.com

存档