歌顿


  • 首页

  • 分类

  • 关于

  • 归档

  • 标签

webpack 懒加载方案

发表于 2017-09-02

懒加载

官网解释:懒加载或者按需加载,是一种很好的优化网页或应用的方式。这种方式实际上是先把你的代码在一些逻辑断点处分离开,然后在一些代码块中完成某些操作后,立即引用或即将引用另外一些新的代码块。这样加快了应用的初始加载速度,减轻了它的总体体积,因为某些代码块可能永远不会被加载。

示例

我们增加一个交互,当用户点击按钮的时候用 console 打印一些文字。但是会等到第一次交互的时候再加载那个代码块(print.js)

src/print.js

1
2
3
4
5
console.log('The print.js module has loaded! See the network tab in dev tools...');
export default () => {
console.log('Button Clicked: Here\'s "some text"!');
}

src/main.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const commonCss = require('./css/common.css');
const greeter = require('./Greeter.js');
document.querySelector("#root").style.color = 'red';
document.querySelector("#root").appendChild(greeter());
function component() {
var element = document.createElement('div');
var button = document.createElement('button');
button.innerHTML = 'Click me and look at the console!';
//重点,ES6 语法
button.onclick = e => import(/* webpackChunkName: "print" */ './print').then(module => {
var print = module.default;
print();
});
element.appendChild(button);
return element;
}
document.body.appendChild(component());

尝试运行 npm run build 命令,可以看到 print,js 模块并未加载,点击交互后才加载,达到目的。

以上是 webpack 的写法,对于 vue 框架有配套成熟的方案。

vue 路由懒加载

vue 框架的懒加载方案需要配合 vue-router 来使用。仅需要两步:

结合 Vue 的 异步组件 和 Webpack 的 code splitting feature, 轻松实现路由组件的懒加载。

  1. 定义一个能够被 webpack 自动代码分割的异步组件
1
const Foo = () => import('./Foo.vue')
  1. 在路由配置中什么都不需要改变,只需要像往常一样使用 Foo:
1
2
3
4
5
const router = new VueRouter({
routes: [
{ path: '/foo', component: Foo }
]
})

原理解释:

首先,可以将异步组件定义为返回一个 Promise 的工厂函数(该函数返回的Promise应该 resolve 组件本身):

1
const Foo = () => Promise.resolve({ /* 组件定义对象 */ })

第二,在 webpack 2中,我们可以使用动态 import语法来定义代码分块点(split point):

1
import('./Foo.vue') // returns a Promise

结合以上两步就是我们缩略的一句。

对比一下原先不用异步的写法:

1
2
3
4
5
6
7
import check from '@/components/check'
const router = new VueRouter({
routes: [
{ path: '/foo', component: Foo }
]
})

webpack 学习总结

发表于 2017-08-22

源码链接: 点我点我

为什要使用WebPack

现今的很多网页其实可以看做是功能丰富的应用,它们拥有着复杂的JavaScript代码和一大堆依赖包。为了简化开发的复杂度,前端社区涌现出了很多好的实践方法,比如说 模块化,Scss 和 less等CSS预处理器。

这些改进大大提高了我们的开发效率,但是利用它们开发的文件往往需要进行额外的处理才能让浏览器识别,而手动处理又是非常繁琐而且麻烦,这就为WebPack类的工具的出现提供了需求。

什么是Webpack

WebPack可以看做是模块打包机:它做的事情是,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Scss,TypeScript等),并将其转换和打包为合适的格式供浏览器使用。

开始使用Webpack

千言万语不如实际操作一番,你会发现 webpack 不过如此。

安装

新建一个空的练习文件夹,在终端中转到该文件夹后执行下述指令就可以完成安装.

1
2
3
4
//全局安装
npm install -g webpack
//安装到你的项目目录
npm install --save-dev webpack

正式使用Webpack前的准备

  1. 在终端中执行一下命令,会创建一个 package.json 文件,这是一个标准的npm说明文件,里面蕴含了丰富的信息,包括当前项目的依赖模块,自定义的脚本任务等等。
1
npm init

输入这个命令后,终端会问你一系列诸如项目名称,项目描述,作者等信息,不过不用担心,如果你不准备在npm中发布你的模块,这些问题的答案都不重要,回车默认即可。

  1. 接着安装 Webpack 作为依赖包

    1
    2
    // 安装Webpack
    npm install --save-dev webpack
  2. 回到之前的空文件夹,并在里面创建两个文件夹,app 文件夹和public文件夹,app文件夹用来存放原始数据和我们将写的JavaScript模块,public文件夹用来存放之后供浏览器读取的文件(包括使用webpack打包生成的js文件以及一个index.html文件)。接下来我们再创建三个文件:

  3. 1
    2
    3
    index.html --放在public文件夹中;
    Greeter.js-- 放在app文件夹中;
    main.js-- 放在app文件夹中;

此时项目结构如下图所示
image

在index.html 文件中写入最基础的html代码,它在这里目的在于引入打包后的js文件

1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Webpack Sample Project</title>
</head>
<body>
<div id='root'>
</div>
<script src="bundle.js"></script>
</body>
</html>

在Greeter.js中定义一个返回包含问候信息的html元素的函数,并依据CommonJS规范导出这个函数为一个模块:

1
2
3
4
5
6
// Greeter.js
module.exports = function() {
var greet = document.createElement('div');
greet.textContent = "Hi there and greetings!";
return greet;
};

main.js文件中我们写入下述代码,用以把 Greeter 模块返回的节点插入页面。

1
2
3
//main.js
const greeter = require('./Greeter.js');
document.querySelector("#root").appendChild(greeter());

正式使用Webpack

经过前面的准备工作,我们现在就开始使用 Webpack,在终端中输出:

1
2
3
4
5
# webpack 全局安装
webpack
# webpack 非全局安装的情况
node_modules/.bin/webpack app/main.js public/bundle.js

结果如下:

image

可以看出webpack同时编译了main.js 和 Greeter,js,现在打开 index.html,可以看到如下结果:

image

说明我们已经成功用 webpack 打包了一个文件!但是我们一个项目中不仅仅打包一个文件这么简单,因此我们需要配置文件来处理复杂的开发需求。

通过配置文件来使用Webpack

当前练习文件夹的根目录下新建一个名为 webpack.config.js 的文件,我们在其中写入如下所示的简单配置代码

1
2
3
4
5
6
7
8
const path = require('path');
module.exports = {
entry: "./app/main.js", //唯一入口文件
output: {
path: path.resolve(__dirname, 'public'), //打包后的文件存放的地方
filename: 'bundle.js' //打包后输出文件的文件名
}
}

有了这个配置之后,再打包文件,只需在终端里运行webpack命令就可以了,这条命令会自动引用 webpack.config.js 文件中的配置选项。

NPM 脚本(NPM Scripts)

考虑到用 CLI 这种方式来运行本地的 webpack 不是特别方便,我们可以设置一个快捷方式。在 package.json 添加一个 npm 脚本(npm script):

package.json

1
2
3
4
5
6
7
{
...
"scripts": {
"build": "webpack"
},
...
}

以后我们可以执行 npm run build 来打包项目。

webpack 基础功能就是如此,但是 webpack 这时候还是一个残缺品,因为它只能处理 js 文件,还不能处理 css、less 或者图片等,Webpack 是通过一个叫 loader 的东西来解决这个问题。

Loaders

默认情况下 webpack 只认识js文件,如果你在 js 中 require 进一个 css 模块是无法打包成功的,这时候 loaders 出现了,它的作用就是将 css/scss/jpg 等文件转换成 module 并添加到依赖图中。

使用 loaders

Loaders 需要单独安装并且需要在 webpack.config.js 中的 modules 关键字下进行配置,下面以 style-loader 为例。

  1. 安装 style-loader

    1
    npm install style-loader --save-dev
  2. webpack.config.js

1
2
3
4
5
6
7
8
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}
]
}
  1. 在 app 目录下新建一个 css 文件夹,并且新建一个 common.css 文件,在 main.js require 进来。

common.css:

1
2
3
#root{
color: red;
}

main.js:

1
2
3
4
const commonCss = require('./css/common.css');
const greeter = require('./Greeter.js');
document.querySelector("#root").appendChild(greeter());

运行 npm start 后再打开 index.html ,发现字体颜色已经变成红色。

读者可以尝试将 style-loader 去掉看看报错信息。

还有图片loader、font loader 等等都可以在官网的教程中提到:
Webpack-guide

Webpack 不仅仅有 loader,还有另一个强大的功能——plugin

plugin

插件(Plugins):用来拓展Webpack功能的,它们会在整个构建过程中生效,执行相关的任务。

与loader 的比较

Loaders和Plugins常常被弄混,但是他们其实是完全不同的东西,可以这么来说,loaders是在打包构建过程中用来处理源文件的(Scss,Less..),一次处理一个,插件并不直接操作单个文件,它直接对整个构建过程其作用。

插件使用

比如说 webpack-dev-server 插件,该插件提供了一个简易的 web Server 和热更新功能。

  1. 安装
1
npm install --save-dev webpack-dev-server
  1. webpack.config.js
1
2
3
4
5
devServer: {
contentBase: "./public",//本地服务器所加载的页面所在的目录
hot: true //开启热更新功能
}
  1. package.json
1
2
3
4
5
{
"scripts": {
"start": "webpack-dev-server --open"
},
}

我们在命令行中输入 npm start 后,会看到浏览器自动加载我们的页面。如果改变了源代码,web server 会在编译后自动重载页面,也就是热更新功能。

PS:webpack-dev-middleware 内置于 webpack-dev-server 中,但它是一个独立的插件,用处是将 webpack 处理过的文件发送给服务器。我们抛弃 webpack-dev-server 插件,运用 webpack-dev-middleware 插件能够获得更多自定义的设置。比如说跟 express 服务器结合。其中 vue-cli 的脚手架就是这么做的。其中的好处待研究,官网有教程。


以上内容解释了什么是 loader 和 plugin,但是日常项目中我们会遇到多页面的情况,解决这个问题我们就需要了解 Entry 这个概念。


Entry

Webpack 创建了一个包含所有依赖的图,这个图的起点就是 entry point。entry point 告诉 webpack 在依赖图中的打包起始点和打包内容。你可以将 entry 看做是 webpack 打包的起始点。

一个最简单的例子就是:

1
2
3
4
module.exports = {
entry: "./app/main.js"
}

多页面

现在来解决我们多页面开发的问题

准备工作

在 app 目录下新增一个 hello.js 文件,在 public 文件下新增一个 temple.html 文件。

hello.js

1
document.querySelector("#root").style.color = 'red';;

temple.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Sample 2</title>
<style>
</style>
</head>
<body>
<div id='root'>
hello world
</div>
</body>
</html>

开始配置

  1. 多入口 Entry 配置信息
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    module.exports = {
    entry: {
    app:'./app/main.js',
    hello: './app/hello.js'
    },
    output: {
    path: path.resolve(__dirname, 'public'), //打包后的文件存放的地方
    filename: '[name].bundle.js'
    }
    }

通过 npm run build 命令可以发现生成了 app.bundle.js 和 hello.bunlde.js 文件,我们打开 public 文件夹下面的生成的 html 文件就可以看到分别打包后效果。

  1. HtmlWebpackPlugin

为了避免每次都要在 html 文件中修改 js 名称和路径,有一个 HtmlWebpackPlugin 帮助我们自动化了这个过程。

2.1 安装

1
npm install --save-dev html-webpack-plugin

2.2 webpack.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const HtmlWebpackPlugin = require('html-webpack-plugin');
plugins: [
// OccurenceOrderPlugin is needed for webpack 1.x only
new HtmlWebpackPlugin({
template: 'temple.html', //模板文件
chunks: ['app'] //设置未入口的配置的字段
}),
new HtmlWebpackPlugin({
template: 'temple.html',
chunks: ['hello'],
filename: 'index-2.html'
})
],

注意:根据官网的解释,我们想要生成 html 页面的数量要等于 HtmlWebpackPlugin 的数量
执行 npm run start,可以看出在 public 文件夹下生成了两个文件:index.html 和 index-2.html。

总结

至此,我们简单的过了一遍 Webpack 的使用场景、如何使用的问题,以及重要概念如 loader,plugin 和 entry,并且做到每个概念都有一个应用场景来解释其意义所在。

写到后面才发现其实 webpack 官网的教程已经很详细了,并且有相应的中文文档,帮助初学者快速上手,也省去了去网上找一些良莠不齐教程的时间。

参考资料。

  • 官方文档
  • 入门 Webpack,看这篇就够了
  • html-webpack-plugin 插件实现多入口多 html 的疑惑

跨域

发表于 2017-08-15

1. 为什么要研究这个问题:

浏览器的同源策略,出于防范跨站脚本的攻击,禁止客户端脚本(如 JavaScript)对不同域的服务进行跨站调用。

2. 什么叫跨域?

当一个资源从与该资源本身所在的服务器不同的域或端口不同的域或不同的端口请求一个资源时,资源会发起一个跨域 HTTP 请求。

比如,站点 http://domain-a.com 的某 HTML 页面通过 的 src 请求 http://domain-b.com/image.jpg。

TIPS:跨域请求并非是浏览器限制了发起跨站请求,而是请求可以正常发起,到达服务器端,但是服务器返回的结果会被浏览器拦截。

3.怎么解决?

3.1 通过JSONP跨域

基本原理:网页通过添加一个

create-object

发表于 2017-08-14

工厂模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function createPerson(name, age, job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
alert(this.name);
};
return o;
}
var person1 = createPerson("Nicholas", 29, "Software Engineer");
var person2 = createPerson("Greg", 27, "Doctor");
person1.sayName(); //"Nicholas"
person2.sayName(); //"Greg"

根据接受的参数创建一个包括所有必要信息的对象,但是无法解决对象识别的问题

构造函数模式

像 Object 和 Array 这样的原生构造函数,在运行时会自动出现在执行环境中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function(){
alert(this.name);
};
}
var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");
person1.sayName(); //"Nicholas"
person2.sayName(); //"Greg"
alert(person1 instanceof Object); //true
alert(person1 instanceof Person); //true
alert(person2 instanceof Object); //true
alert(person2 instanceof Person); //true
alert(person1.constructor == Person); //true
alert(person2.constructor == Person); //true
alert(person1.sayName == person2.sayName); //false

过程如下:

  1. 创建一个新对象
  2. 将构造函数的作用域赋给新对象,所以 this 指向新对象。
  3. 执行构造函数中的代码,为这个新对象添加属性。
  4. 返回新对象

主要问题:因为 sayName 这个函数被创建了两个???。。。

原型模式

每个函数都有一个 prototype(原型)属性,这个属性是指针,指向一个对象,包含可以由特定类型的所有实例共享的属性和方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
function Person(){
}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
alert(this.name);
};
var person1 = new Person();
person1.sayName(); //"Nicholas"
var person2 = new Person();
person2.sayName(); //"Nicholas"
alert(person1.sayName == person2.sayName); //true
alert(Person.prototype.isPrototypeOf(person1)); //true
alert(Person.prototype.isPrototypeOf(person2)); //true
//only works if Object.getPrototypeOf() is available
if (Object.getPrototypeOf){
alert(Object.getPrototypeOf(person1) == Person.prototype); //true
alert(Object.getPrototypeOf(person1).name); //"Nicholas"
}

…所有实例共享同一个属性和方法。

构造函数模式 + 原型模式

最常见的方式就是这个,构造函数用于定义实例属性,原型模式用于定义方法和共享的属性。
默认就是这中啦。。。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.friends = ["Shelby", "Court"];
}
Person.prototype = {
constructor: Person,
sayName : function () {
alert(this.name);
}
};
var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");
person1.friends.push("Van");
alert(person1.friends); //"Shelby,Court,Van"
alert(person2.friends); //"Shelby,Court"
alert(person1.friends === person2.friends); //false
alert(person1.sayName === person2.sayName); //true

CSS常见布局

发表于 2017-08-11

总结一下常见的网页布局。

两列: 定宽 + 自适应

HMLT 代码:

1
2
3
4
5
6
7
8
9
<div class="parent">
<div class="left">
<p>left</p>
</div>
<div class="right">
<p>right</p>
<p>right</p>
</div>
</div>
  • 方式一:float + margin
1
2
3
4
5
6
7
.left{
float: left;
width: 100px;
}
.right{
margin-left: 120px;
}

这种情况不会遇到浮动问题,不定宽+自适应才会遇到。

  • 方式二:flex
1
2
3
4
5
6
7
8
9
10
.parent{
display: flex;
}
.left{
width: 100px;
margin-right: 20px;
}
.right{
flex: 1;
}

‘ flex: 1’ 表示每个 flex 项沿主轴的可用空间大小,因为默认值是0,所以设置 1 就占用了剩余横轴空间。并且占用的空间是在设置 padding 和 margin 之后剩余的空间。

不定宽 + 自适应

HMLT 代码:

1
2
3
4
5
6
7
8
9
<div class="parent">
<div class="left">
<p>left</p>
</div>
<div class="right">
<p>right</p>
<p>right</p>
</div>
</div>
  • 方式一:float + overflow
    这种方式会出现浮动问题,因为左侧元素的高度不确定。
1
2
3
4
5
6
7
8
9
10
11
.left{
float: left;
margin-right: 20px;
}
//为什么是右侧设置啊。。
.right{
overflow: hidden;
}
.left p{
width: 200px;
}

利用BFC的规则来清楚浮动!

  • 方式二:flex
    同一

等宽

HTML 代码:

1
2
3
4
5
6
<div class="parent">
<div class="column"><p>1</p></div>
<div class="column"><p>2</p></div>
<div class="column"><p>3</p></div>
<div class="column"><p>4</p></div>
</div>
  • 方式一: witdh

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    .parent{
    margin-left: -20px;
    }
    .column{
    float: left;
    width: 25%;
    padding-left: 20px;
    box-sizing: border-box;
    }

    box-sizing 用来修复 padding 造成的塌陷情况。

    • 方式二:
1
2
3
4
5
6
7
8
9
10
.parent{
display: flex;
}
.column{
flex: 1;
}
.column+.column{
margin-left:20px;
}

全屏布局

从现实需求理解正则表达式

发表于 2017-06-30

在 JavaScript中,正则表达式也是对象。

如何创建一个正则表达式?

方法一:使用一个正则表达式字面量,其由包含在斜杠之间的模式组成,如下所示:

1
const regex = /abc/;

方法二:调用RegExp对象的构造函数

1
let regex = new RegExp("ab+c");// 本文不对 RegExp对象 做介绍

如何使用?

方法一:简单模式,由简单的字符所构成的。如 /abc/

例子:

1
let isEqual = /13566668888/.test(value);

只要 value 含有 “13566668888” 这串数字,isEqual 就为 true。

Demo地址

方法二: 使用特殊字符,如 /^abc$/

例子:

1
let isEqual = /^13566668888/.test(value)

只要 value 开头前9个字符前必须是 “13566668888”,isEqual 才为 true,

Demo地址

平时开发中,常用的是特殊字符。

特殊字符:$

1
let isEqual = /13566668888$/.test(value);

只要当 value 后面9个字符是 “13566668888” ,isEqual 就为 true;

Demo地址

需求1

如何匹配一个9个数字的手机号码?

方法一:

1
let isEqual = /^1[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]$/.test(value);

其中方括号”[]” 表示查找方括号之间的任何字符,方括号里面 0-9 表示在 0到9的数字都可以匹配到,对于 [a-z] 同样,可以匹配到字符 a 到字符 z之间的任意字符。

但显然这个方式很繁琐。

元字符:\d

1
let isEqual = /^1\d\d\d\d\d\d\d\d\d\d$/.test(value);

元字符:”\d” 表示数字,但还是很繁琐。。

量词

1
let isEqual = /^1\d{10}$/.test(value);

\d{10} 表示匹配包含10个数字的字符串,n{X} 匹配包含 X 个 n 的序列的字符串

需求2

如何匹配邮箱?

方法:

1
let isEqual = /.+@163\.com$/.test(value);

知识点: 元字符 + 量词 + 转移符

“.” 表示查找单个字符

“+” 表示匹配任何包含至少一个 n 的字符串。

“\ .” 反斜杠表示将其后的特殊字符转义为字面量。

因此上述正则表达式的意思是,先匹配 n个字符串,然后再匹配 @163,等将小数点”.” 转义后,再匹配 “.com” 结尾,这样就能完成匹配一个邮箱的需求。

Demo地址

需求3

那如何匹配不同的邮箱呢?

方法:

1
let isEqual = /(.+)@(163|126|188)\.com$/.test(value);

知识点:

(.+), 匹配 ‘.+’ 并且记住匹配项,括号被称为捕获括号。

(163|126|188),“|” 表示匹配‘163’或者‘188’。

本文从三个需求入手,带领读者直观地感受正则表达式对于匹配字符串的作用。更多的特殊字符的用法请参考文档即可。

参考资料

W3c-正则表达式

MDN-正则表达式

HTML5全屏背景视频与 CSS 和 JS(插件或库)

发表于 2017-06-12

译文原链接:http://codetheory.in/html5-fullscreen-background-video/

前言:

当网页载入时,自动播放的全屏背景视频 已经成为当前颇受欢迎的趋势。 就个人而言,我认为自动播放、质量好的视频会增加用户/客户的参与度。应该记住,视频的故事必须与品牌相关。 近年来,我们被FB和Twitter等社交网络上的大量视频所包围。 据研究由此引起了用户更多的参与。

最近我不得不在一个网站上实现相同的功能,因此我决定构建一个用到 HTML5 视频元素并且易于使用的基于JS的插件,这个插件常在一个容器内部显示背景视频。 我已经将所有代码以及工作演示放在 Github- Bideo.js 上。现在让我们来讨论一下在执行过程中所发生的一切事情,以及我在途中学习到了什么。

基本实现

你会认为这些实现非常简单。 将HTML5 video 元素投入容器内并指定100%的宽度和高度。 好的,似乎合理但是不行,这有个例子: DEMO

您会注意到背景视频样式是中心对齐,并在两侧留下了大量的空白。 这是因为当您缩放(或调整大小)视频元素时,它会按比例更改其尺寸。 所以如果一个视频是400×200,那么即使你的尺寸设置为500×400,也不会这样显示出来。 它将被调整到500×250(按比例)。

所以在这种情况下会发生的事是:首先实现高度100%的样式,并相应地计算宽度,因此最终出现的效果不是父容器的100%。

因此这是我遇到的第一个问题,我开始通过查看不同站点的其他实现来找出解决方案。我希望视频可以被设置为 CSS 的背景样式,然后我们可以像 background-size:cover / contains 这样的东西,但是不行,这是不可能的(也许将来)。不过这是我们需要的样式。 有些人认为这段代码会有用:

1
2
3
4
5
6
7
8
9
10
11
12
13
#container {
overflow: hidden;
height: 400px;
background: #edeae8;
position: relative;
}
video {
width: 100%; height: 100%;
position: absolute;
top: 0; left: 0; right: 0; bottom: 0;
}

但这没啥用! 您会注意到出现相同的效果(按比例缩放),除了这一次它左对齐,而不是中心对齐 Demo 。

在这一点上,有些人可能会想知道为什么我一定要对父容器指定一个固定的高度。 这是因为在你浏览器有指定的宽高情况下,比如说 1920 1080,此刻有一个 同等尺寸的视频那肯定很方便,但如果来一个 2000 2000 分辨率的视频呢?你如何确保这个视频能够完整塞进浏览器中?因此非常有必要解决这个问题。

解决方法

似乎JavaScript是解决这个问题的唯一方法。 这个想法是获取容器的具体尺寸(宽度和高度),以及视频的尺寸,综合起来用来将视频缩放,确保它覆盖整个容器。 当然,由于比例重新调整,视频实际尺寸上可能比容器大,但没关系。 实际上,当视频宽或高被裁剪掉一些部分时,它看起来并不糟糕。 我正在谈论的功能正是HTML5 中的 ==background-size: cover== 属性 所要做的事情,这意味着如果容器的尺寸与图像的尺寸不成比例,其中一个尺寸将被剪切。

因此,在同一演示中,如果我们必须调整视频大小,确保完全覆盖该容器,代码如下:

HTML:

1
2
3
4
5
<div id="container">
<video autoplay muted loop>
<source src="source.mp4" type="video/mp4">
</video>
</div>

CSS:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/* CSS */
* {
margin: 0; padding: 0;
}
#container {
overflow: hidden;
height: 400px;
background: #edeae8;
position: relative;
}
video {
position: absolute;
/* Vertical and Horizontal center*/
left: 50%; top: 50%;
transform: translate(-50%, -50%);
}

最后,JS 代码展示了视频尺寸如何根据窗口尺寸动态的调整大小:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// JS
var video = document.querySelector('video')
, container = document.querySelector('#container');
var setVideoDimensions = function () {
// Video's intrinsic dimensions
var w = video.videoWidth
, h = video.videoHeight;
// Intrinsic Ratio
// Will be more than 1 if W > H and less if W < H
var videoRatio = (w / h).toFixed(2);
// Get the container's computed styles
//
// Also calculate the min dimensions required (this will be
// the container dimentions)
var containerStyles = window.getComputedStyle(container)
, minW = parseInt( containerStyles.getPropertyValue('width') )
, minH = parseInt( containerStyles.getPropertyValue('height') );
// What's the min:intrinsic dimensions
//
// The idea is to get which of the container dimension
// has a higher value when compared with the equivalents
// of the video. Imagine a 1200x700 container and
// 1000x500 video. Then in order to find the right balance
// and do minimum scaling, we have to find the dimension
// with higher ratio.
//
// Ex: 1200/1000 = 1.2 and 700/500 = 1.4 - So it is best to
// scale 500 to 700 and then calculate what should be the
// right width. If we scale 1000 to 1200 then the height
// will become 600 proportionately.
var widthRatio = minW / w
, heightRatio = minH / h;
// Whichever ratio is more, the scaling
// has to be done over that dimension
if (widthRatio > heightRatio) {
var newWidth = minW;
var newHeight = Math.ceil( newWidth / videoRatio );
}
else {
var newHeight = minH;
var newWidth = Math.ceil( newHeight * videoRatio );
}
video.style.width = newWidth + 'px';
video.style.height = newHeight + 'px';
};
video.addEventListener('loadedmetadata', setVideoDimensions, false);
window.addEventListener('resize', setVideoDimensions, false);

最终效果: Demo

如果您阅读了JS代码中的注释,你会明白放大/缩小视频大小的数学原理。

注意:在这种测试中,我们在HTML中指定了视频源,但理想情况下,您应该通过JS向视频添加 ==source== 标签,这是因为如果从缓存中获取视频或者在本地进行测试,有时候 ==loadedmetadata== 这事件不会被触发,这是因为浏览器有尽快加载视频的策略

遮罩

一旦视频能自动播放并且美观大方的效果实现后,另一个常见的效果要求是在视频上方显示一个遮罩,使得视频上的内容(标题,文字等)对于用户来说是相当可见和可读的。 这不过是写些简单的 HTML / CSS 而已 ,因此我并没有实现这个作为我 插件 的一部分。 你需要做的是,添加一个覆盖元素到您的容器和样式如下所示:

1
2
3
4
5
6
#overlay {
position: absolute;
top: 0; right: 0; left: 0; bottom: 0;
background: rgba(0,0,0,0.5);
}

这有个例子: Demo

视频封面

另一个用 HTML/CSS 就可以容易实现的效果就是视频封面。你需要的只是添加一个元素到视频元素后面:

1
<div id="video_cover"></div>

然后添加 css:

1
2
3
4
5
6
7
8
9
#video_cover {
position: absolute;
width: 100%; height: 100%;
background: url('video_cover.jpeg') no-repeat;
background-size: cover;
background-position: center;
}

这有两个重点需要注意:

  1. 我们使用了 background-size:cover,这正是我们调整视频大小的代码。 我们的逻辑模拟了 cover 的行为,因为它使视频覆盖了容器的整个宽度或高度,并且超过容器的尺寸被限制。
  2. 我们使用了 background-position:center,因为我们也将视频水平和垂直居中。 假设video_cover.jped 表示视频的第一帧,并且重要的是它能正确的摆放把视频盖住住,这样一旦我们隐藏并播放视频就不会因为各种元素的位置变化而扰乱图像/视频。

影片加载后,即可隐藏此封面。 Bideo.js 接受一个 ==onLoad== 回调,您可以在此处执行此操作。 这个回调在 ==canplay== 事件中执行。

网速

在网络连接慢的情况下尽量不要播放视频。这样做的理想方法是使用JavaScript来检测网络速度,然后不播放视频(而是显示图像)。当然,这应该很应该的方式,只要有一个JS浏览器API(当前)给我们的用户的互联网速度或可靠的方式来检测用户的连接速度。但不幸的是,检测用户连接的互联网速度是一项艰巨的任务,大多数时间不可靠。

为了检测速度,我们必须下载一个相当大的文件(至少5-10mb),这肯定不行。即使我们这样做,DNS查找,数据包交换和用户和目标服务器之间的跳数等因素会使结果(检测到速度)偏移。

抛开检测用户的连接速度这个方向,如果我们可以实现一个优化的播放方案,假设我们在一段时间内缓冲视频 - 比如说5s ,缓冲完后开始播放,播放完5s 后停止等待下一个 5s 缓冲好的视频再播放。这注意看起来不错。但问题是这个想法很难实现,这是因为浏览器的 api 设计不同造成的。

即使我们实施了一个逻辑:“加载5s的数据需要多少秒”来决定可接受的加载速度,它可能无法按照您的期望工作,因为我注意到,对于较慢连接chrome(v50)即使有足够的缓冲播放时间(约10-15秒),仍然会阻止回放进入缓存区。(译者注:暂时无法理解这个方法,所以不会翻译这段,为啥要决定可接受的加载速度??)

目前还没有办法计算出在给定时间段内加载的视频的内容大小(目的是为了检测速度,用来最终设置可接受的加载速率来播放视频)。

所以如果你尝试不同的逻辑,你至少会看到在当前的 API 里(视频属性,方法和事件),很难找到一个解决方案。希望今后更容易。

HTML5视频选项

我写的插件不支持几个选项(或配置),因为HTML5视频标签本身很受支持。 我基本上是指音量,静音,循环,播放,自动播放等选项。您可以在去 MDN 找到音频/视频方法,事件和属性的详尽列表。

注意事项

在网站上整合这种格式的视频时,请务必牢记某些内容:

  1. 视频质量应该很好,并使用与您的品牌相关的品质。
  2. 你最有可能会自动播放您的视频,但不会播放声音,因为这可能会让用户感到烦人。 如果需要,请取消静音按钮。
  3. 我建议使用超级压缩视频(原因很明显),长度约为30-60秒。 如果长度太短,那么它可以感觉到奇怪,因为它循环(除非你停止一次玩,这也可能是奇怪的 - 一个短视频停止太早)。

结论

因此,在主页中或任何其他特定容器中实现全屏幕背景视频超级简单。 只需使用Bideo.js并在几分钟内实现您想要的。 在下面的评论中,让我知道你对插件或用法的看法。

JavaScript最佳实践

发表于 2017-05-03

1. 可维护性

1.1 什么是可维护性

可维护性的代码有一些特征:

  1. 可理解性——其他人可以接受代码并理解它的意图和一般途径,无需原开发人员完整解释

  2. 直观性——代码中的东西一看就明白,无论操作多么复杂

  3. 可适应性——代码以一种数据上的变化不要求完全重写方法

  4. 可扩展性——在代码架构上考虑到在未来对核心功能扩展

  5. 可调试性——有地方出错,代码给予足够的信息直接地确定问题所在啊

1.2 代码约定

以下小节讨论代码约定的概论

1.2.1 可读性

  • 缩进:常见的缩进大小为4个空格,

  • 注释:

    1. 函数和方法——每个函数或方法都应该包含一个注释,描述其目的和用于完成任务用到的算法。事先的假设也很重要,如参数代表什么,函数是否有返回值。
    2. 大段代码——完成单个任务的多行代码应该在前面放一个描述任务的注释
    3. 复杂算法——如果使用一种独特的方式解决问题,应该解释一下你是如何做的,不仅帮助其他浏览你代码的人,也帮助自己下次理解
    4. Hack——不要假设其他人在看代码能够理解 hack 所要应付的浏览器问题,直接写出。

1.2.2 变量和函数命名

  1. 变量和函数应该使用合乎逻辑的名字,不用担心长度。长度问题可以通过后处理和压缩来缓解。

  2. 函数名应该以动词开始

1.2.3 变量类型透明

由于 js 中变量是松散类型,很容易忘记变量所应包含的数据类型。合适的命名方式可以一定程度上缓解这问题。

初始化。当定义一个变量后就应该被初始化为一个值,来暗示将来如何用。

1
2
3
4
5
//通过初始化指定变量类型
var found = flase; //布尔型
var count = -1; //数字
var name = ""; //字符串
var person = null; //对象

1.3 松散耦合

只要应用的某个部分过分依赖于另外一个部分,代码就是耦合过紧。因为 Web 应用设计的技术,有多种情况使它耦合过紧

1. 解耦 HTML/JavaScript:
HTML 是数据,JavaScript 是行为,记住这两点。

1
2
3
4
5
//将 HTML 紧密耦合到 JavaScript
function insert(msg){
var container = document.getElementById("container");
container.innerHTML = "<div> aa </div>";
}

当使用上面这个例子,有一个页面布局问题,可能和动态创建的 HTML 没有被正确格式化有关。不过要定位这个错误非常困难,因为你可能一般先看到页面的源代码来查找那烦人的 HTML,但是没有找到,因为他是动态生成的。因此一般来说,应该避免在 JavaScript 中大量创建 HTML。一定要保持层次分离,这样可以很容易确定错误来源。

一般在页面中直接包含并隐藏标记,等整个页面渲染好之后,再用 javaScript 显示该标志,而非生成它。

2. 解耦 CSS/JavaScript
最常见耦合的例子就是使用 JavaScript 来更改某些样式

1
2
3
//CSS 对 JavaScript 的紧密耦合
element.style.color = "red";
element.style.BackgroundColor = "blue";

由于 CSS 主要负责页面的显示,当显示出现任何问题都应该只是查看 CSS 文件来解决,然而使用 JavaScript 来更改样式就出现了第二个可能已更改和检查的地方。结果 JavaScript 也负责了页面的显示。如果未来需要修改样式表,那 CSS 和 JavaScript 文件都要修改,这是一个噩梦。

可以让耦合更松散,通过动态更改样式类而非特定的样式来实现。

1
2
//CSS 对 JavaScript 的松散耦合
element.className = "edit";

3. 解耦应用逻辑/事件处理程序
每个 Web 应用都有相当多的事件处理程序,然而很少能有将应用逻辑从事件处理程序中分离的。请看例子

1
2
3
4
5
6
7
8
9
10
function handleKeyPress(event){
event = EventUtil.getEvent(event);
if(event.keyCode == 13){
var target = EventUtil.getTarget(event);
var value = pareInt(target.value);
if(value > 5){
doucument.getElementById("msg").style.display = "block";
}
}
}

这个事件处理程序既包含应用逻辑,又进行事件的处理。这让问题变得双重性,一是如果没有预想的结果,那是因为事件处理程序没有被调用还是逻辑失败?二是如果一个后续的事件引发同样的应用逻辑,那还得复制功能代码。

较好的改动是将应用逻辑和事件处理程序分离。

1
2
3
4
5
6
7
8
9
10
11
12
13
function handleValue(value){
value = parseInt(value);
if(value > 5){
doucument.getElementById("msg").style.display = "block";
}
}
function handleKeyPress(event){
event = EventUtil.getEvent(event);
if(event.keyCode == 13){
var target = EventUtil.getTarget(event);
handleValue(target.value);
}
}

注意函数 handleValue 中没有任何东西会依赖于任何事件处理程序,它只是接收一个值,并根据该值处理。

以下是应牢记的应用和业务逻辑之间松散耦合的几条原则:

  • 不要将 event 对象传给其他方法;只传来自 event 对象所需的数据

  • 任何事件处理程序都应该处理事件,然后讲处理转交给应用逻辑

  • 任何可以在应用层面的动作都应该可以在不执行任何事件处理程序下进行

1.4 编程实践

书写可维护的 Js 并不仅仅关于如何格式化代码,还关系到代码做什么问题。在企业中往往有大量人员一起创作一个 Web 应用。这种情况下目标是是确保每个人所使用的浏览器环境都有一致和不变的规则。

1. 尊重对象所有权。(最重要)

它的意思是你不能修改不属于你的对象。简单来说,如果你不负责创建或维护某个对象、它的对象或者它的方法,那么你就不能对他们进行修改。更具体来说:

  • 不要为实例或原型添加属性;

  • 不要为实例或原型添加方法;

  • 不要重定义已存在的方法;

著名的 Prototype JavaScript 库就出现过这种例子:它为 document 对象实现了 getElementsByClassName() 方法,返回一个 Array 的实例并增加了一个 each() 方法。使用该库的人习惯

1
document.getElementsByClassName("msg").each();

….BOOM

你可以通过以下方式为对象创建新的功能:

  • 创建包含所需功能的新对象,并用它与相关对象进行交互。
  • 创建自定义类型,继承需要修改的类型,为它添加功能。

2. 避免全局量

最多创建一个全局变量,让其他对象和函数存在其中。

1
2
3
4
5
//两个全局量——避免!
var name = "Nicholas";
function sayName(){
alert(name);
}

1
2
3
4
5
6
7
//一个全局量——推荐!
var MyApplication = {
name : "Nicholas",
sayName : function(){
alert(this.name);
}
}

消除了前一段代码存在的问题。首先,变量 name 覆盖了 window.name 属性,可能与其他功能产生冲突;其次,它有主消除功能作用域之间的混淆。

命名空间:单一的全局对象 xxx 作为一个容器,其中定义了其他对象。用这种方式将功能组合在一起的对象。用来确定每个人都统一使用的全局对象的名字,并且尽可能唯一。

1
2
3
4
5
6
7
8
//创建全局对象
var Wrox = {};
//为 Professional JavaScript 创建命名空间
Wrox.ProJs = {};
//将书中用到的对象附加上去
Wrox.ProJs.EventUtil = {..};

避免了其他库的命名冲突。

3. 避免与 null 进行比较

直接将值与 null比较是使用过度的,并且由于不充分的类型检测导致错误。

1
2
3
4
5
function sortArray(values){
if(values != null){
values.sort();
}
}

这里的 if 语句有其他值可以通过,包括字符串、数字、它们会导致函数跑出错误。因为 JavaScript 不做任何自动的类型检查。

修改为:

1
2
3
4
5
function sortArray(values){
if( values instanceof Array){
values.sort();
}
}

这个版本可以阻止所有非法值,完全用不着 null。

如果看到了与 null 比较代码,尝试使用以下技术替换:

  • 值应为引用类型,使用 instanceof 操作符检查其构造函数;

  • 值应为基本类型,使用 typeof 检查其类型;

  • 如果希望对象包含某个特定的方法名,则使用 typeof 操作符确保指定名字的方法存在于对象上。

代码中 null 比较越少,越容易确定代码的目的。

4. 使用常量

1
2
3
var Constans = {
Url: xxxx
};

JavaScript原型及原型链

发表于 2017-04-27

一、原型

原型是 ECMAScript 实现继承的过程中产生的一个概念。

继承:
java 中:指在已有的一个类基础上创建新类的过程。
ES:指在一个对象的基础上创建新对象的过程。原型指在这过程中作为基础的对象。

创建原型

// 鸟对象
var bird = {
    name: "bird",

    fly: function () {
        console.log("fly");
    }
}

假设我们需要一个鹰对象,因为我们已经有一个鸟对象,因此可以从这个鸟对象继承信息。

//鹰对象
var eagle = Object.create(bird);
eagle.fly(); // fly

通过 Object.create() 方法我们传入了鸟对象,作为鹰对象的原型来创建鹰对象,然后鹰对象中就产生了一个叫 _proto_ 的指针,这指针指向鸟对象。通过这个指针鹰对象就可以访问到鸟对象的 fly() 方法,当然编译器帮我们自动处理了这个指针访问的过程。

但是对于原型来讲,prototype 属性是很重要的存在,下面来讲讲 prototype。

二、prototype 属性

从一个例子讲 prototype 属性存在解决了什么问题。
现在有一个叫做 DOG 的构造函数,表示狗对象的原型。

function DOG(name){
    this.name = name;
}

对这个构造函数使用new,就会生成一个狗对象的实例。

var dogA = new DOG('大毛');
alert(dogA.name); // 大毛

new运算符的缺点

但是,用构造函数生成实例对象,有一个缺点,那就是无法共享属性和方法。

比如,在DOG对象的构造函数中,设置一个实例对象的共有属性species。

function DOG(name){
    this.name = name;
    this.species = '犬科';
}

然后,生成两个实例对象:

var dogA = new DOG('大毛');
var dogB = new DOG('二毛');

这两个对象的species属性是独立的,修改其中一个,不会影响到另一个。

dogA.species = '猫科';
alert(dogB.species); // 显示"犬科",不受dogA的影响

每一个实例对象,都有自己的属性和方法的副本。这不仅无法做到数据共享,也是极大的资源浪费。

prototype属性的引入

考虑到这一点,Brendan Eich 决定为构造函数设置一个 prototype 属性。

这个属性包含一个对象(以下简称” prototype 对象”),所有实例对象需要共享的属性和方法,都放在这个对象里面;那些不需要共享的属性和方法,就放在构造函数里面。

实例对象一旦创建,将自动引用 prototype 对象的属性和方法。也就是说,实例对象的属性和方法,分成两种,一种是本地的,另一种是引用的。

还是以 DOG 构造函数为例,现在用 prototype 属性进行改写:

function DOG(name){
    this.name = name;
}
DOG.prototype = { species : '犬科' };

var dogA = new DOG('大毛');
var dogB = new DOG('二毛');

alert(dogA.species); // 犬科
alert(dogB.species); // 犬科

现在,species属性放在prototype对象里,是两个实例对象共享的。只要修改了prototype对象,就会同时影响到两个实例对象。

DOG.prototype.species = '猫科';

alert(dogA.species); // 猫科
alert(dogB.species); // 猫科

三、原型链

讲原型一个不可避免的概念就是原型链,原型链是通过前面两种创建原型的方式 Object.create() 或 DOG.prototype 时生成的一个 _proto_ 指针来实现的。

以 DOG 为例讲原型链

img

红色的箭头就是原型链。DOG 对象有一个 prototype 对象,而实例对象 dogA 通过一个 _proto_ 对象引用这个 prototype 对象。

可以看出 dogA 能访问到的 species 属性实际上是在 DOG 的原型电源线 prototype 中,因此才能实现实例对象属性共享访问却不能修改。

但是在 DOG.prototype 中还存在一个 _proto_ 属性,这又是指向谁呢?

看图
img
指向 Object 对象,这样 DOG 对象就拥有 Object 对象中原型属性和方法。比如说 toString() 就在其中。

还有一点,那就是 Js 的函数也是对象啊,我们每个创建的函数其实也继承了一个函数对象,而函数则继承了 Object 对象。。
image

以上就是一个简单的 Dog 类完整的原型链。

总结一下原型链作用:对象属性的访问修改和删除。

  1. 访问。优先在对象本身查找,没有则顺着原型链向上查找
  2. 修改。只能修改跟删除自身属性,不会影响到原型链上的其他对象。

四、总结

由于所有的实例对象共享同一个 prototype 对象,那么从外界看起来,prototype 对象就好像是实例对象的原型,而实例对象则好像”继承”了 prototype 对象一样。

表单(待填

发表于 2017-04-25

表单的基础知识

  • 提交表单

  • 重置表单

  • 表单字段

    • 共有的表单字段属性

    • 共有的表单字段方法

    • 共有的表单字段事件

文本框脚本

  • 选择文本

    • 选择事件

    • 取得选择的文本

    • 选择部分文本
  • 过滤输入

    • 屏蔽字符

    • 操作剪贴板(跳过)

  • 自动切换焦点

  • H5约束验证 API

    • 必填字段

    • 其他输入类型

    • 检测有效性
    • 禁用验证

选择框脚本

  • 选择选项

  • 添加选项

  • 移除选项
  • 移动和重拍选项

表单序列化

富文本编辑

12
歌顿

歌顿

12 日志
2 标签
© 2017 歌顿
由 Hexo 强力驱动
主题 - NexT.Mist