果你是 JavaScript 的新手,一些像 “module bundlers vs module loaders”、“Webpack vs Browserify” 和 “AMD vs.CommonJS” 这样的术语,很快让你不堪重负。
JavaScript 模块系统可能令人生畏,但理解它对 Web 开发人员至关重要。
在这篇文章中,我将以简单的言语(以及一些代码示例)为你解释这些术语。 希望这对你有会有帮助!
好作者能将他们的书分成章节,优秀的程序员将他们的程序划分为模块。
就像书中的章节一样,模块只是文字片段(或代码,视情况而定)的集群。然而,好的模块是高内聚低松耦的,具有不同的功能,允许在必要时对它们进行替换、删除或添加,而不会扰乱整体功能。
使用模块有利于扩展、相互依赖的代码库,这有很多好处。在我看来,最重要的是:
1)可维护性: 根据定义,模块是高内聚的。一个设计良好的模块旨在尽可能减少对代码库部分的依赖,这样它就可以独立地增强和改进,当模块与其他代码片段解耦时,更新单个模块要容易得多。
回到我们的书的例子,如果你想要更新你书中的一个章节,如果对一个章节的小改动需要你调整每一个章节,那将是一场噩梦。相反,你希望以这样一种方式编写每一章,即可以在不影响其他章节的情况下进行改进。
2)命名空间: 在 JavaScript 中,顶级函数范围之外的变量是全局的(这意味着每个人都可以访问它们)。因此,“名称空间污染”很常见,完全不相关的代码共享全局变量。
在不相关的代码之间共享全局变量在开发中是一个大禁忌。正如我们将在本文后面看到的,通过为变量创建私有空间,模块允许我们避免名称空间污染。
3)可重用性:坦白地说:我们将前写过的代码复制到新项目中。 例如,假设你从之前项目编写的一些实用程序方法复制到当前项目中。
这一切都很好,但如果你找到一个更好的方法来编写代码的某些部分,那么你必须记得回去在曾经使用过的其他项目更新它。
这显然是在浪费时间。如果有一个我们可以一遍又一遍地重复使用的模块,不是更容易吗?
有多种方法来创建模块,来看几个:
模块模式用于模拟类的概念(因为 JavaScript 本身不支持类),因此我们可以在单个对象中存储公共和私有方法和变量——类似于在 Java 或 Python 等其他编程语言中使用类的方式。这允许我们为想要公开的方法创建一个面向公共的 API,同时仍然将私有变量和方法封装在闭包范围中。
有几种方法可以实现模块模式。在第一个示例中,将使用匿名闭包,将所有代码放在匿名函数中来帮助我们实现目标。(记住:在 JavaScript 中,函数是创建新作用域的唯一方法。)
例一:匿名闭包
(function () {
// 将这些变量放在闭包范围内实现私有化
var myGrades=[93, 95, 88, 0, 55, 91];
var average=function() {
var total=myGrades.reduce(function(accumulator, item) {
return accumulator + item}, 0);
return '平均分 ' + total / myGrades.length + '.';
}
var failing=function(){
var failingGrades=myGrades.filter(function(item) {
return item < 70;});
return '挂机科了 ' + failingGrades.length + ' 次。';
}
console.log(failing()); // 挂机科了次
}());
使用这个结构,匿名函数就有了自己的执行环境或“闭包”,然后我们立即执行。这让我们可以从父(全局)命名空间隐藏变量。
这种方法的优点是,你可以在这个函数中使用局部变量,而不会意外地覆盖现有的全局变量,但仍然可以访问全局变量,就像这样:
var global='你好,我是一个全局变量。)';
(function () {
// 将这些变量放在闭包范围内实现私有化
var myGrades=[93, 95, 88, 0, 55, 91];
var average=function() {
var total=myGrades.reduce(function(accumulator, item) {
return accumulator + item}, 0);
return '平均分 ' + total / myGrades.length + '.';
}
var failing=function(){
var failingGrades=myGrades.filter(function(item) {
return item < 70;});
return '挂机科了 ' + failingGrades.length + ' 次。';
}
console.log(failing()); // 挂机科了次
onsole.log(global); // 你好,我是一个全局变量。
}());
注意,匿名函数的圆括号是必需的,因为以关键字 function 开头的语句通常被认为是函数声明(请记住,JavaScript 中不能使用未命名的函数声明)。因此,周围的括号将创建一个函数表达式,并立即执行这个函数,这还有另一种叫法 立即执行函数(IIFE)。如果你对这感兴趣,可以在这里了解到更多。
例二:全局导入
jQuery 等库使用的另一种流行方法是全局导入。它类似于我们刚才看到的匿名闭包,只是现在我们作为参数传入全局变量:
(function (globalVariable) {
// 在这个闭包范围内保持变量的私有化
var privateFunction=function() {
console.log('Shhhh, this is private!');
}
// 通过 globalVariable 接口公开下面的方法
// 同时将方法的实现隐藏在 function() 块中
globalVariable.each=function(collection, iterator) {
if (Array.isArray(collection)) {
for (var i=0; i < collection.length; i++) {
iterator(collection[i], i, collection);
}
} else {
for (var key in collection) {
iterator(collection[key], key, collection);
}
}
};
globalVariable.filter=function(collection, test) {
var filtered=[];
globalVariable.each(collection, function(item) {
if (test(item)) {
filtered.push(item);
}
});
return filtered;
};
globalVariable.map=function(collection, iterator) {
var mapped=[];
globalUtils.each(collection, function(value, key, collection) {
mapped.push(iterator(value));
});
return mapped;
};
globalVariable.reduce=function(collection, iterator, accumulator) {
var startingValueMissing=accumulator===undefined;
globalVariable.each(collection, function(item) {
if(startingValueMissing) {
accumulator=item;
startingValueMissing=false;
} else {
accumulator=iterator(accumulator, item);
}
});
return accumulator;
};
}(globalVariable));
在这个例子中,globalVariable 是唯一的全局变量。与匿名闭包相比,这种方法的好处是可以预先声明全局变量,使得别人更容易阅读代码。
例三:对象接口
另一种方法是使用立即执行函数接口对象创建模块,如下所示:
var myGradesCalculate=(function () {
// 将这些变量放在闭包范围内实现私有化
var myGrades=[93, 95, 88, 0, 55, 91];
// 通过接口公开这些函数,同时将模块的实现隐藏在function()块中
return {
average: function() {
var total=myGrades.reduce(function(accumulator, item) {
return accumulator + item;
}, 0);
return'平均分 ' + total / myGrades.length + '.';
},
failing: function() {
var failingGrades=myGrades.filter(function(item) {
return item < 70;
});
return '挂科了' + failingGrades.length + ' 次.';
}
}
})();
myGradesCalculate.failing(); // '挂科了 2 次.'
myGradesCalculate.average(); // '平均分 70.33333333333333.'
正如您所看到的,这种方法允许我们通过将它们放在 return 语句中(例如算平均分和挂科数方法)来决定我们想要保留的变量/方法(例如 myGrades)以及我们想要公开的变量/方法。
例四:显式模块模式
这与上面的方法非常相似,只是它确保所有方法和变量在显式公开之前都是私有的:
var myGradesCalculate=(function () {
// 将这些变量放在闭包范围内实现私有化
var myGrades=[93, 95, 88, 0, 55, 91];
var average=function() {
var total=myGrades.reduce(function(accumulator, item) {
return accumulator + item;
}, 0);
return'平均分 ' + total / myGrades.length + '.';
};
var failing=function() {
var failingGrades=myGrades.filter(function(item) {
return item < 70;
});
return '挂科了' + failingGrades.length + ' 次.';
};
// Explicitly reveal public pointers to the private functions
// that we want to reveal publicly
return {
average: average,
failing: failing
}
})();
myGradesCalculate.failing(); // '挂科了 2 次.'
myGradesCalculate.average(); // '平均分 70.33333333333333.'
这可能看起来很多,但它只是模块模式的冰山一角。 以下是我在自己的探索中发现有用的一些资源:
所有这些方法都有一个共同点:使用单个全局变量将其代码包装在函数中,从而使用闭包作用域为自己创建一个私有名称空间。
虽然每种方法都有效且都有各自特点,但却都有缺点。
首先,作为开发人员,你需要知道加载文件的正确依赖顺序。例如,假设你在项目中使用 Backbone,因此你可以将 Backbone 的源代码 以<script> 脚本标签的形式引入到文件中。
但是,由于 Backbone 对 Underscore.js 有很强的依赖性,因此 Backbone 文件的脚本标记不能放在Underscore.js 文件之前。
作为一名开发人员,管理依赖关系并正确处理这些事情有时会令人头痛。
另一个缺点是它们仍然会导致名称空间冲突。例如,如果两个模块具有相同的名称怎么办?或者,如果有一个模块的两个版本,并且两者都需要,该怎么办?
幸运的是,答案是肯定的。
有两种流行且实用的方法:CommonJS 和 AMD。
CommonJS 是一个志愿者工作组,负责设计和实现用于声明模块的 JavaScript API。
CommonJS 模块本质上是一个可重用的 JavaScript,它导出特定的对象,使其可供其程序中需要的其他模块使用。 如果你已经使用 Node.js 编程,那么你应该非常熟悉这种格式。
使用 CommonJS,每个 JavaScript 文件都将模块存储在自己独立的模块上下文中(就像将其封装在闭包中一样)。 在此范围内,我们使用 module.exports 导出模块,或使用 require 来导入模块。
在定义 CommonJS 模块时,它可能是这样的:
function myModule() {
this.hello=function() {
return 'hello!';
}
this.goodbye=function() {
return 'goodbye!';
}
}
module.exports=myModule;
我们使用特殊的对象模块,并将函数的引用放入 module.exports 中。这让 CommonJS 模块系统知道我们想要公开什么,以便其他文件可以使用它。
如果想使用 myModule,只需要使用 require 方法就可以,如下:
var myModule=require('myModule');
var myModuleInstance=new myModule();
myModuleInstance.hello(); // 'hello!'
myModuleInstance.goodbye(); // 'goodbye!'
与前面讨论的模块模式相比,这种方法有两个明显的好处:
另外需要注意的是,CommonJS 采用服务器优先方法并同步加载模块。 这很重要,因为如果我们需要三个其他模块,它将逐个加载它们。
现在,它在服务器上运行良好,但遗憾的是,在为浏览器编写 JavaScript 时使用起来更加困难。 可以这么说,从网上读取模块比从磁盘读取需要更长的时间。 只要加载模块的脚本正在运行,它就会阻止浏览器运行其他任何内容,直到完成加载,这是因为 JavaScript 是单线程且 CommonJS 是同步加载的。
CommonJS一切都很好,但是如果我们想要异步加载模块呢? 答案是 异步模块定义,简称 AMD。
使用 AMD 的加载模块如下:
define(['myModule', 'myOtherModule'], function(myModule, myOtherModule) {
console.log(myModule.hello());
});
define 函数的第一个参数是一个数组,数组中是依赖的各种模块。这些依赖模块在后台(以非阻塞的方式)加载进来,一旦加载完毕,define 函数就会调用第二个参数,即回调函数执行操作。
接下来,回调函数接收参数,即依赖模块 - 示例中就是 myModule 和 myOtherModule - 允许函数使用这些依赖项, 最后,所依赖的模块本身也必须使用 define 关键字来定义。例如,myModule如下所示:
define([], function() {
return {
hello: function() {
console.log('hello');
},
goodbye: function() {
console.log('goodbye');
}
};
});
因此,与 CommonJS 不同,AMD 采用浏览器优先的方法和异步行为来完成工作。 (注意,有很多人坚信在开始运行代码时动态加载文件是不利的,我们将在下一节关于模块构建的内容中探讨更多内容)。
除了异步性,AMD 的另一个好处是模块可以是对象,函数,构造函数,字符串,JSON 和许多其他类型,而CommonJS 只支持对象作为模块。
也就是说,和CommonJS相比,AMD不兼容io、文件系统或者其他服务器端的功能特性,而且函数包装语法与简单的require 语句相比有点冗长。
对于同时支持 AMD 和 CommonJS 特性的项目,还有另一种格式:通用模块定义(Universal Module Definition, UMD)。
UMD 本质上创造了一种使用两者之一的方法,同时也支持全局变量定义。因此,UMD 模块能够同时在客户端和服务端同时工作。
简单看一下 UMD 是怎样工作的:
(function (root, factory) {
if (typeof define==='function' && define.amd) {
// AMD
define(['myModule', 'myOtherModule'], factory);
} else if (typeof exports==='object') {
// CommonJS
module.exports=factory(require('myModule'), require('myOtherModule'));
} else {
// Browser globals (Note: root is window)
root.returnExports=factory(root.myModule, root.myOtherModule);
}
}(this, function (myModule, myOtherModule) {
// Methods
function notHelloOrGoodbye(){}; // A private method
function hello(){}; // A public method because it's returned (see below)
function goodbye(){}; // A public method because it's returned (see below)
// Exposed public methods
return {
hello: hello,
goodbye: goodbye
}
}));
Github 上 enlightening repo 里有更多关于 UMD 的例子。
你可能已经注意到,上面的模块都不是 JavaScript 原生的。相反,我们已经创建了通过使用模块模式、CommonJS 或 AMD 来模拟模块系统的方法。
幸运的是,TC39(定义 ECMAScript 的语法和语义的标准组织)一帮聪明的人已经引入了ECMAScript 6(ES6)的内置模块。
ES6 为导入导出模块提供了很多不同的可能性,已经有许多其他人花时间解释这些,下面是一些有用的资源:
与 CommonJS 或 AMD 相比,ES6 模块最大的优点在于它能够同时提供两方面的优势:简明的声明式语法和异步加载,以及对循环依赖项的更好支持。
也许我个人最喜欢的 ES6 模块功能是它的导入模块是导出时模块的实时只读视图。(相比起 CommonJS,导入的是导出模块的拷贝副本,因此也不是实时的)。
下面是一个例子:
// lib/counter.js
var counter=1;
function increment() {
counter++;
}
function decrement() {
counter--;
}
module.exports={
counter: counter,
increment: increment,
decrement: decrement
};
// src/main.js
var counter=require('../../lib/counter');
counter.increment();
console.log(counter.counter); // 1
在这个例子中,我们基本上创建了两个模块的对象:一个用于导出它,一个在我们需要的时候引入。
此外,在 main.js 中的对象目前是与原始模块是相互独立的,这就是为什么即使我们执行 increment 方法,它仍然返回 1,因为引入的变量和最初导入的变量是毫无关联的。需要改变你引入的对象唯一的方式是手动执行增加:
counter.counter++;
console.log(counter.counter); // 2
另一方面,ES6创建了我们导入的模块的实时只读视图:
// lib/counter.js
export let counter=1;
export function increment() {
counter++;
}
export function decrement() {
counter--;
}
// src/main.js
import * as counter from '../../counter';
console.log(counter.counter); // 1
counter.increment();
console.log(counter.counter); // 2
超酷?我发现这一点是因为ES6允许你可以把你定义的模块拆分成更小的模块而不用删减功能,然后你还能反过来把它们合成到一起, 完全没问题。
总体上看,模块打包只是将一组模块(及其依赖项)以正确的顺序拼接到一个文件(或一组文件)中的过程。正如 Web开发的其它方方面面,棘手的问题总是潜藏在具体的细节里。
将程序划分为模块时,通常会将这些模块组织到不同的文件和文件夹中。 有可能,你还有一组用于正在使用的库的模块,如 Underscore 或 React。
因此,每个文件都必须以一个 <script> 标签引入到主 HTML 文件中,然后当用户访问你的主页时由浏览器加载进来。 每个文件使用 <script> 标签引入,意味着浏览器不得不分别逐个的加载它们。
这对于页面加载时间来说简直是噩梦。
为了解决这个问题,我们将所有文件打包或“拼接”到一个大文件(或视情况而定的几个文件),以减少请求的数量。 当你听到开发人员谈论“构建步骤”或“构建过程”时,这就是他们所谈论的内容。
另一种加速构建操作的常用方法是“缩减”打包代码。 缩减是从源代码中移除不必要的字符(例如,空格,注释,换行符等)的过程,以便在不改变代码功能的情况下减少内容的整体大小。
较少的数据意味着浏览器处理时间会更快,从而减少了下载文件所需的时间。 如果你见过具有 “min” 扩展名的文件,如 “underscore-min.js” ,可能会注意到与完整版相比,缩小版本非常小(不过很难阅读)。
除了捆绑和/或加载模块之外,模块捆绑器还提供了许多其他功能,例如在进行更改时生成自动重新编译代码或生成用于调试的源映射。
构建工具(如 Gulp 和 Grunt)能为开发者直接进行拼接和缩减,确保为开发人员提供可读代码,同时有利于浏览器执行的代码。
当你使用一种标准模块模式(上部分讨论过)来定义模块时,拼接和缩减文件非常有用。 你真正在做的就是将一堆普通的 JavaScript 代码捆绑在一起。
但是,如果你坚持使用浏览器无法解析的非原生模块系统(如 CommonJS 或 AMD(甚至是原生 ES6模块格式)),则需要使用专门工具将模块转换为排列正确、浏览器可解析的代码。 这就是 Browserify,RequireJS,Webpack 和其他“模块打包工具”或“模块加载工具”的用武之地。
除了打包和/或加载模块之外,模块打包器还提供了许多其他功能,例如在进行更改时生成自动重新编译代码或生成用于调试的源映射。
下面是一些常见的模块打包方法:
正如前面所知道的,CommonJS以同步方式加载模块,这没有什么问题,只是它对浏览器不实用。我提到过有一个解决方案——其中一个是一个名为 Browserify 的模块打包工具。Browserify 是一个为浏览器编译 CommonJS模块的工具。
例如,有个 main.js 文件,它导入一个模块来计算一组数字的平均值:
var myDependency=require(‘myDependency’);
var myGrades=[93, 95, 88, 0, 91];
var myAverageGrade=myDependency.average(myGrades);
在这种情况下,我们有一个依赖项(myDependency),使用下面的命令,Browserify 以 main.js 为入口把所有依赖的模块递归打包成一个文件:
browserify main.js -o bundle.js
Browserify 通过跳入文件分析每一个依赖的 抽象语法树(AST),以便遍历项目的整个依赖关系图。一旦确定了依赖项的结构,就把它们按正确的顺序打包到一个文件中。然后,在 html 里插入一个用于引入 “bundle.js” 的 <script> 标签,从而确保你的源代码在一个 HTTP 请求中完成下载。
类似地,如果有多个文件且有多个依赖时,只需告诉 Browserify 的入口文件路径即可。最后打包后的文件可以通过 Minify-JS 之类的工具压缩打包后的代码。
如果你正在使用 AMD,你需要使用像 RequireJS 或者 Curl 这样的 AMD 加载器。模块加载器(与模块打包工具不同)会动态加载程序需要运行的模块。
提醒一下,AMD 与 CommonJS 的主要区别之一是它以异步方式加载模块。 从这个意义上说,对于 AMD,从技术上讲,实际上并不需要构建步骤,因为异步加载模块意味着在运行过程中逐步下载那些程序所需要的文件,而不是用户刚进入页面就一下把所有文件都下载下来。
但实际上,对于每个用户操作而言,随着时间的推移,大容量请求的开销在生产中没有多大意义。 大多数 Web 开发人员仍然使用构建工具打包和压缩 AMD 模块以获得最佳性能,例如使用 RequireJS 优化器,r.js 等工具。
总的来说,AMD 和 CommonJS 在打包方面的区别在于:在开发期间,AMD 可以省去任何构建过程。当然,在代码上线前,要使用优化工具(如 r.js)进行优化。
就打包工具而言,Webpack 是一个新事物。它被设计成与你使用的模块系统无关,允许开发人员在适当的情况下使用 CommonJS、AMD 或 ES6。
你可能想知道,为什么我们需要 Webpack,而我们已经有了其他打包工具了,比如 Browserify 和 RequireJS,它们可以完成工作,并且做得非常好。首先,Webpack 提供了一些有用的特性,比如 “代码分割”(code splitting) —— 一种将代码库分割为“块(chunks)”的方式,从而能实现按需加载。
例如,如果你的 Web 应用程序,其中只需要某些代码,那么将整个代码库都打包进一个大文件就不是很高效。 在这种情况下,可以使用代码分割,将需要的部分代码抽离在"打包块",在执行按需加载,从而避免在最开始就遇到大量负载的麻烦。
代码分割只是 Webpack 提供的众多引人注目的特性之一,网上有很多关于 “Webpack 与 Browserify 谁更好” 的激烈讨论。以下是一些客观冷静的讨论,帮助我稍微理清了头绪:
当前 JS 模块规范(CommonJS, AMD) 与 ES6 模块之间最重要的区别是 ES6 模块的设计考虑到了静态分析。这意味着当你导入模块时,导入的模块在编译阶段也就是代码开始运行之前就被解析了。这允许我们在运行程序之前移,移除那些在导出模块中不被其它模块使用的部分。移除不被使用的模块能节省空间,且有效地减少浏览器的压力。
一个常见的问题,使用一些工具,如 Uglify.js ,缩减代码时,有一个死码删除的处理,它和 ES6 移除没用的模块又有什么不同呢?只能说 “视情况而定”。
死码消除(Dead codeelimination)是一种编译器原理中编译最优化技术,它的用途是移除对程序运行结果没有任何影响的代码。移除这类的代码有两种优点,不但可以减少程序的大小,还可以避免程序在运行中进行不相关的运算行为,减少它运行的时间。不会被运行到的代码(unreachable code)以及只会影响到无关程序运行结果的变量(Dead Variables),都是死码(Dead code)的范畴。
有时,在 UglifyJS 和 ES6 模块之间死码消除的工作方式完全相同,有时则不然。如果你想验证一下, Rollup’s wiki 里有个很好的示例。
ES6 模块的不同之处在于死码消除的不同方法,称为“tree shaking”。“tree shaking” 本质上是死码消除反过程。它只包含包需要运行的代码,而非排除不需要的代码。来看个例子:
假设有一个带有多个函数的 utils.js 文件,每个函数都用 ES6 的语法导出:
export function each(collection, iterator) {
if (Array.isArray(collection)) {
for (var i=0; i < collection.length; i++) {
iterator(collection[i], i, collection);
}
} else {
for (var key in collection) {
iterator(collection[key], key, collection);
}
}
}
export function filter(collection, test) {
var filtered=[];
each(collection, function(item) {
if (test(item)) {
filtered.push(item);
}
});
return filtered;
}
export function map(collection, iterator) {
var mapped=[];
each(collection, function(value, key, collection) {
mapped.push(iterator(value));
});
return mapped;
}
export function reduce(collection, iterator, accumulator) {
var startingValueMissing=accumulator===undefined;
each(collection, function(item) {
if(startingValueMissing) {
accumulator=item;
startingValueMissing=false;
} else {
accumulator=iterator(accumulator, item);
}
});
return accumulator;
}
接着,假设我们不知道要在程序中使用什么 utils.js 中的哪个函数,所以我们将上述的所有模块导入main.js中,如下所示:
import * as Utils from ‘./utils.js’;
最终,我们只用到的 each 方法:
import * as Utils from ‘./utils.js’;
Utils.each([1, 2, 3], function(x) { console.log(x) });
“tree shaken” 版本的 main.js 看起来如下(一旦模块被加载后):
function each(collection, iterator) {
if (Array.isArray(collection)) {
for (var i=0; i < collection.length; i++) {
iterator(collection[i], i, collection);
}
} else {
for (var key in collection) {
iterator(collection[key], key, collection);
}
}
};
each([1, 2, 3], function(x) { console.log(x) });
注意:只导出我们使用的 each 函数。
同时,如果决定使用 filte r函数而不是每个函数,最终会看到如下的结果:
import * as Utils from ‘./utils.js’;
Utils.filter([1, 2, 3], function(x) { return x===2 });
tree shaken 版本如下:
function each(collection, iterator) {
if (Array.isArray(collection)) {
for (var i=0; i < collection.length; i++) {
iterator(collection[i], i, collection);
}
} else {
for (var key in collection) {
iterator(collection[key], key, collection);
}
}
};
function filter(collection, test) {
var filtered=[];
each(collection, function(item) {
if (test(item)) {
filtered.push(item);
}
});
return filtered;
};
filter([1, 2, 3], function(x) { return x===2 });
此时,each 和 filter 函数都被包含进来。这是因为 filter 在定义时使用了 each。因此也需要导出该函数模块以保证程序正常运行。
我们知道 ES6 模块的加载方式与其他模块格式不同,但我们仍然没有讨论使用 ES6 模块时的构建步骤。
遗憾的是,因为浏览器对 ES6模 块的原生支持还不够完善,所以现阶段还需要我们做一些补充工作。
下面是几个在浏览器中 构建/转换 ES6 模块的方法,其中第一个是目前最常用的方法:
作为 web 开发人员,我们必须经历很多困难。转换语法优雅的ES6代码以便在浏览器里运行并不总是容易的。
问题是,什么时候 ES6 模块可以在浏览器中运行而不需要这些开销?
答案是:“尽快”。
ECMAScript 目前有一个解决方案的规范,称为 ECMAScript 6 module loader API。简而言之,这是一个纲领性的、基于 Promise 的 API,它支持动态加载模块并缓存它们,以便后续导入不会重新加载模块的新版本。
它看起来如下:
// myModule.js
export class myModule {
constructor() {
console.log('Hello, I am a module');
}
hello() {
console.log('hello!');
}
goodbye() {
console.log('goodbye!');
}
}
// main.js
System.import(‘myModule’).then(function(myModule) {
new myModule.hello();
});
// ‘hello!’
你亦可直接对 script 标签指定 “type=module” 来定义模块,如:
<script type="module">
// loads the 'myModule' export from 'mymodule.js'
import { hello } from 'mymodule';
new Hello(); // 'Hello, I am a module!'
</script>
更加详细的介绍也可以在 Github 上查看:es-module-loader
此外,如果您想测试这种方法,请查看 SystemJS,它建立在 ES6 Module Loader polyfill 之上。 SystemJS 在浏览器和 Node 中动态加载任何模块格式(ES6模块,AMD,CommonJS 或 全局脚本)。
它跟踪“模块注册表”中所有已加载的模块,以避免重新加载先前已加载过的模块。 更不用说它还会自动转换ES6模块(如果只是设置一个选项)并且能够从任何其他类型加载任何模块类型!
对于日益普及的 ES6 模块,下面有一些有趣的观点:
对于 HTTP/1,每个TCP连接只允许一个请求。这就是为什么加载多个资源需要多个请求。有了 HTTP/2,一切都变了。HTTP/2 是完全多路复用的,这意味着多个请求和响应可以并行发生。因此,我们可以在一个连接上同时处理多个请求。
由于每个 HTTP 请求的成本明显低于HTTP/1,因此从长远来看,加载一组模块不会造成很大的性能问题。一些人认为这意味着模块打包不再是必要的,这当然有可能,但这要具体情况具体分析了。
例如,模块打包还有 HTTP/2 没有好处,比如移除冗余的导出模块以节省空间。 如果你正在构建一个性能至关重要的网站,那么从长远来看,打包可能会为你带来增量优势。 也就是说,如果你的性能需求不是那么极端,那么通过完全跳过构建步骤,可以以最小的成本节省时间。
总的来说,绝大多数网站都用上 HTTP/2 的那个时候离我们现在还很远。我预测构建过程将会保留,至少在近期内。
一旦 ES6 成为模块标准,我们还需要其他非原生模块规范吗?
我觉得还有。
Web 开发遵守一个标准方法进行导入和导出模块,而不需要中间构建步骤——网页开发长期受益于此。但 ES6 成为模块规范需要多长时间呢?
机会是有,但得等一段时间 。
再者,众口难调,所以“一个标准的方法”可能永远不会成为现实。
希望这篇文章能帮你理清一些开发者口中的模块和模块打包的相关概念,共进步。
)我们来看看什么是模块化?
模块化是一种将系统分离成独立功能部分的方法,可将系统分割成独立的功能部分,严格定义模块接口、模块间具有透明性。javascript中的模块在一些C、PHP、java中比较常见:
c中使用include 包含.h文件;php中使用require_once包含.php文件
java使用import导入包
此中都有模块化的思想。
2)模块化的优缺点:
a>优点:可维护性
1.灵活架构,焦点分离
2.方便模块间组合、分解
3.方便单个模块功能调试、升级
4.多人协作互不干扰
可测试性
1.可分单元测试
b>缺点:
性能损耗
1.系统分层,调用链会很长
2.模块间通信,模块间发送消息会很耗性能
......虽然优缺点很明显,但毕竟从协助和高效角度来讲,还是值的学习的;下面就直接进入正题吧,讲一下如何写简单的js模块。(ps:本人是菜鸟,只能写简单的)
第一种当然是最最简单的写法,可能学习前端的都会在无形中写过的---那就是函数方法:即
模块就是实现特定功能的一组方法。
只要把不同的函数(以及记录状态的变量)简单地放在一起,就算是一个模块。
原始写法
上面的函数m1()和m2(),组成一个模块。使用的时候,直接调用就行了。
这种做法的缺点很明显:"污染"了全局变量,无法保证不与其他模块发生变量名冲突,而且模块成员之间看不出直接关系。
第二种难度稍微升级,需要面向对象的操作了;主要目的是为了解决上面的缺点,可以把模块写成一个对象,所有的模块成员都放到这个对象里面。
上面的函数m1()和m2(),都封装在module1对象里。使用的时候,就是调用这个对象的属性。
但是,这样的写法会暴露所有模块成员,内部状态可以被外部改写。比如,外部代码可以直接改变内部计数器的值。
第三种:立即执行函数法;使用"立即执行函数"(Immediately-Invoked Function Expression,IIFE),可以达到不暴露私有成员的目的。
使用上面的写法,外部代码无法读取内部的_count变量。
module1就是Javascript模块的基本写法。
接下来的方法都是正对module1的优化和加工的写法;
a):放大模式。
如果一个模块很大,必须分成几个部分,或者一个模块需要继承另一个模块,这时就有必要采用"放大模式"(augmentation)。
上面的代码为module1模块添加了一个新方法m3(),然后返回新的module1模块。
b):宽放大模式(Loose augmentation)。
在浏览器环境中,模块的各个部分通常都是从网上获取的,有时无法知道哪个部分会先加载。如果采用上一节的写法,第一个执行的部分有可能加载一个不存在空对象,这时就要采用"宽放大模式"。
与"放大模式"相比,"宽放大模式"就是"立即执行函数"的参数可以是空对象。
上面的模式就是一些简单的写法,期待与各位同学一起学习进步;再者感谢阮一峰老师的《Javascript模块化编程(一):模块的写法》文章,让我受益匪浅。最后再盗一张阮一峰老师的图片!
程序员HTML5培训教程-html和css的使用方法以及样式,很好的基础知识系列,内容都是干货知识点,适合收藏下来!
布局步骤
第一步: 清除默认样式
第二步: 划分模块
第三步: 设置模块的大小以及位置
第四步: 划分下一级模块
html和css
引入网页头像
<link rel="shortcut icon" href="img/...ico">
css样式表的引入方式
css样式表的引入方式
1、外链式
<link href="" rel="stylesheet">
2、嵌入式
<style></style>
3、行内样式
<div style="width:200px;height:200pxs;"></div>
文件命名以及变量命名
命名规范
1、严格区分大小写
2、可以采用字母数字下划线$,数字不开头
3、命名语义化
4、可以采用驼峰命名法
清除默认样式
清楚边距
*{
margin: 0;
padding: 0;
list-style: none;
}
a标签清楚下划线和颜色
a{
color: black;
text-decoration: none;
}
css中颜色的表示方式
css中颜色的表示方式:
1.预定义的颜色【关键字颜色】 red pink blue yellow
2.#6位数的色值 #00-00-00 红绿蓝
3.rgb(红,绿,蓝) :rgb([0-255],[0-255],[0-255])
4.rgba(red,green,blue,透明度) :rgba([0-255],[0-255],[0-255],[0-1])
0-1: 0全透明,1不透明
html中的标签和属性
html:
标签:
按照语法分类:
1.单标签:只有开始标签
meta img a
2.双标签:有开始标签和结束标签
<html></html>
3.属性的语法
语法:
属性名="属性值"
属性名="属性值1 属性值2"
注意:
1、标签名和属性名之间要有空格
2、多个属性之间要有空格
3、多个属性值之间要有空格
4.开始标签 标签名后有空格
?
按照标签在页面中的呈现效果分类:
1、行内元素
行内元素定义:在一行内显示,只能设置左右间距,不可以设置上下间距。
举例:span del i em b strong a(title="鼠标移入时显示的文字";target=" "(新窗口打开的位置 _self:在本窗口打开;_blank:在新窗口打开) ...
2、块元素
块元素定义:可以设置宽高,独占一行。
举例:div 标题标签 列表标签 段落标签 ...
3、行内块元素
行内块元素定义:可以设置宽高,在一行显示。
举例:img 【title="鼠标移入时显示的文字" 】 表单控件
元素的转换
块元素: display:block;
行内块元素:display:inline-block;
行内元素: display:inline;
元素的级别
块元素 > 行内块元素 > 行内元素
元素嵌套规范
1、同一级别可以相互嵌套
2、级别高的元素可以嵌套级别低的元素
3、段落标签只能嵌套行内元素
4、a标签不可以嵌套a标签;p不能嵌套p
盒子模型及其问题
四部构成:
1、margin 外间距 盒子与盒子之间的距离
2、border 边框
3、padding 内填充(内间距) 边框与内容之间的距离。
4、content 内容
margin-top margin-right margin-bottom margin-left
margin: 50px; 上 右 下 左
margin: 50px 100px; 上下 左右
margin:0 auto; auto自动
margin: 50px 100px 150px; 上 左右 下
margin: 50px 100px 150px 200px; 上 右 下 左
border: 1px solid red;
border-top \ border-right \border-bottom \ border-left
border-top-width:上边框的宽度
?
padding:设置方法同margin
?
content: ;
width : 数值 百分比 auto
height: 数值 百分比 auto
?
盒子模型的问题:
1.大部分元素的margin和padding默认为0,但有一部分的margin和padding不为0,例如body 标题标签(h1-h6)(ul ol il等列表标签) 段落标签
2.想领的两个块元素的margin会重合,值会取最大值
3.margin可以为[负数] ,padding不可以设置[负数]。
4.行内元素margin只有左右,没有上下
5.如果(1)发生嵌套关系的元素,(2)父元素没有上边框,(3)上padding ,(4)父元素与子元素之间没有别的内容,此时子元素margin-top就会作用到父元素身上
margin-top的解决方式:
1.用父元素的padding-top代替子元素的margin-top;
2.给父元素添加overflow:hidden;
宽高的设置和计算
height:auto / 百分比 / px;
width:auto / 百分比 / px;
height:auto; 参照与父元素
width:auto;参照与内容
box-sizing:border-box; 将边框算入盒子内;
?
一个元素实际的宽高
实际宽度=border-left + padding-left + width +paddint-right + border-right;
实际高度=border-top + padding-top + height + padding-bottom + border-bottom;
浮动
作用:让块元素横排排列
样式: float:left;从左往右排列
float:right;从右往左排列
原理:让元素脱离文档流,让元素从文档层浮动到浮动层。
引发的问题:父元素不设置高度,子元素都浮动,浮动的子元素撑不开父元素。(浮动的元素脱离文档流)
*解决方式一:给父元素添加 overflow:hidden;(超出部分隐藏)
*解决方式二:在父元素内容最后添加拥有清除浮动属性的元素。
clear:right/left/both ; 别的浮动对它的影响清除掉
例:
.box:after{
content: "";
display:block;
width: 0;
height: 0;
clear:both;
}
*解决方式三:父元素能设置高度的尽量设置高度
浮动之后的块元素参照内容:属性值 auto
定位
定位的元素脱离文档层,到达定位层
定位的元素会多出5个样式:
top right bottom left z-index:999
上 右 下 左 层级(层级越高,离用户越近)【只能在有定位属性的元素上才能用】
层级:
z-index:整数;
定位的几种方式:
1.相对定位:
相对于自身来定位,在文档层中保留原来的位置
用法:
position:relative;
2.绝对定位:
相对于最近的 定位的 祖先元素 来定位,完全脱离文档流(其他顶替其位置)
用法:
position:absolute;
+方向值
3.固定定位:
相对于浏览器的四条边,完全脱离文档流
用法:
position:fixed;
top与bottom同时定义,那个样式会作用到元素身上的判断关系:
top的权重比bottom的权重大
left的权重比right的权重大
元素作用时:
1.如果是
position:relative;
left:;
margin:;
先作用margin,在作用relative;
2.如果是
position:absolute;
left:;
margin:;
先作用absolute,在作用margin;
定位元素的居中方式:
方法一:
1.水平居中:
position:absolute;
left:50%;
margin-left:-自身长度的一半;
2.垂直居中:
position:absolute;
top:50%;
margin-top:-自身长度的一半;
3.绝对居中:
position:absolute;
left:50%;
top:50%;
margin-left:-自身长度的一半;
margin-top:-自身长度的一半;
方法二:
1.水平居中:
position:absolute;
left:0;
right:0
2D和3D
2D和3D属性:
1.平移样式
transform:translate(x,y); 向上为负, 向下为正
transform:translateX(100px);
transform:rotate(180deg) ; (1turn)转一圈
平移 transform:translate() 例子:translate(x,y) translateX()
旋转 transform:rotate() 例子rotate(180deg)顺时针 -180deg 逆时针
transform:rotate()空格translate();
transform-origin:px px;变换的中心点;
left center;
缩放 transform:scale() 例子:scale(2) 放大为原来的2倍 scale(0.n)缩小为原 来的0.n scale(m,n) x轴m,y轴n
斜切 transform:skew() 例子:skew(45peg) 左拉伸45° skew(45peg,m)
全部 时间
3.过渡的属性样式: transition-property: , ;
可以为:属性的全部样式
4.过度的总时间: transition-duration:;
5.过渡的时间函数: transition-timing-function:;
linear(匀速) ease(开头结尾慢,中间快)
cubic-bezier(1,0.07,0.54,0.21) 贝塞尔曲线
6.延迟 transition-delay:;
?
3d效果:和2d的一样transition,transform;
prespective:给父元素加prespective(灭点的值)
prespective-origin:x y;灭点的位置 调整观察的角度(大多数情况不设置)
transform:ratate3d(0-1的值,0-1的值,0-1的值,45deg)
transform:ratateY(45deg)
transform:translate3d(0-1,0-1,px)
?
父元素:transform-style:preserve-3d;
动画
动画规则:
@keyframes 动画名(随便给){
(动画规则)
from{}
to{}
}
@keyframes 动画名(随便给){
(动画规则)
0%{}
50%{}
100%{}
}
@keyframes animation1{
from{
background-color:red;
}
to{
background-color:blue;
}
}
?
挂载动画:将动画加到元素身上
.元素{
animation:animation1 时间 步数 时间函数 延迟时间 次数 ;
}
挂载多个动画:
.元素{
animation:animation1 时间,animation2 时间,animation1 时间;
其他动画的相同的可以附件通过animation属性;
}
animation的样式
动画名:animation-name
时间: animation-duration
步数:animation-steps:8;
时间函数:animation-timing-function
延迟: animation-delay
动画次数: animation-iteration-count:infinite(无限次)/2;
指定下一次动画是否逆向:animation-direction:alternate(逆向)/ normal(常规);
最后的状态:animation-fill-mode:backwards(默认(保持一开始的状态))/forwards(保持当前的状态);
状态即指定动画是否运动: animation-play-state: running(运行)/paused(静止);
?
元素分类
按照在页面中的呈现效果:
1.行内元素:在一行内显示 ,不可以设置宽高 :(存放文字)
span a b i strong del
2.行内块元素:在一行内显示,可以设置宽高:(有缝隙 不常用)
img 表单控件
3.块元素 :可以设置宽高,独占一行
div 标题标签(h1-h6) 列表标签(ul-li ol-li dl>dt+dd 段落标签 (p pre))
元素嵌套规范:
1.同一级别可以相互嵌套
2.级别高的可以嵌套级别低的元素
3.p标签只能嵌套行内元素
4.a链接不能相互嵌套
元素的转换:
1.块元素:display:block;
2.行内块元素:display:inline-block;
3.行内元素:display:inline;
背景图片以及浏览器内核
背景图
先设大小,在引background;
background: url('路径') no-repeat left bottom/contain;
//图片位置 禁止重复 位置(top bottom left right)
4. background-repeat:no-repeat,repeat;(无重复)
5. background-repeat:repeat-x(x方向重复)
background-repeat:repeat-y(y方向重复)
background-position:x y;(数值 方位值(top/bottom left/rightcenter(可以省略)) )
8. padding-box;(默认)从padding位置开始渲染
9. border-box;从边框的位置开始渲染
content-box;从内容的位置开始渲染
11. padding-box;(默认)从padding位置结束渲染
12. border-box;从边框的位置结束渲染
content-box;从内容的位置结束渲染
8.可以简写:
background:空格隔开;
//渐变开始的方向(默认top) 类似25deg(25度)
10.浏览器内核//背景色渐变
1. /* 标准语法 */
例子:background: linear-gradient(top,#3bbcff,#47eaff);
2. /* 谷歌内核 -webkit- */
例子:background: -webkit-linear-gradient(top,#3bbcff,#47eaff);
3. /* 火狐内核 -moz- */
例子:background: -moz-linear-gradient(top,#3bbcff,#47eaff);
4. /* 欧鹏内核 -o- */
例子:background: -o-linear-gradient(top,#3bbcff,#47eaff);
5. /* IE内核 -ms- */
例子:background: -ms-linear-gradient(top,#3bbcff,#47eaff);
?
文件的读取方法路径
绝对路径:从盘符开始的一条完整路径
相对路径:两个文件的位置关系
边框的相关属性【圆角,边框形状】
border-radius:边框的半径 设置圆角 n%或者num像素
border-style:dotted solid double dashed;
上边框是点状
右边框是实线
下边框是双线
左边框是虚线
透明度
透明性的选择:(整个容器都变)
opacity:;0-1之间的值;
字体
font-family=“ 字体” //字体样式可以被继承
鼠标移入样式
span标签
cursor:pointer; 鼠标样式:手型
阴影
box-shadow:x轴偏移量 y轴偏移量 阴影的模糊程度 阴影的大小(0和本身一样大小) 阴影的颜色;
引入字符图标
引入字符图标:
行内元素 随意
span class=“iconfont 图标类名”
可调节样式: 同文字
文档流
文档流:
标准情况下 ,页面元素从左往右 从上往下 依次排列
flex布局(规范的设计稿)-弹性布局
容器(父元素)的属性:【display:flex;】
*flex-direction: 决定主轴方向。
row 主轴在水平方向,从左向右(默认)。
row-reverse 主轴在水平方向,从右向左
column 主轴在垂直方向,从上到下
column-reverse 主轴在垂直方向,从下到上
*flex-wrap: 决定项目换行
wrap: 项目换行
nowrap: 项目不换行(默认值)
wrap-reverse: 项目换行且反转
*justify-content: 决定项目在主轴的对齐方式
flex-start;主轴的起点
flex-end;主轴的终点
center;主轴的中心
space-between;两端对齐
space-around;项目两侧距离相等
*align-items:项目在交叉轴上的对齐方式(适用于一根轴线与多跟轴线)
flex-start:交叉轴的起点
flex-end:交叉轴的终点
Center:交叉轴的中心
baseline: 基线对齐(文本底部)
*align-content:定义项目在交叉轴上的对齐方式(仅适用于多根轴线)
flex-start;交叉轴的起点
flex-end;交叉轴的终点
center;交叉轴的中心
space-between;两端对齐
space-around;两侧距离相等
子元素(项目)的属性:
*order:定义项目的排列顺序,数值越小,越靠前,默认值为0(可以取负值)。
*flex-grow:定义项目的放大比例。默认值为0,即使存在剩余空间,也不放大。
*flex-shrik:定义项目的缩小比例,默认值为1,空间不足,项目缩小;值为0时,空间不足,项目也不缩小.
*flex-basis: 定义项目占据的主轴空间.默认auto或者自己添加像素;
*align-self:定义单个项目在交叉轴的对齐方式.
flex-start:交叉轴的起点
flex-end:交叉轴的终点
Center:交叉轴的中心
滚动条
overflow-x:auto;超出部分在x轴的表现形式。
auto:自动;(如果超出,就自动以滚动条的形式显示)
去滚动条: 加在具有overflow属性的元素身上
::-webkit-scrollbar{
height:0;
}
overflow-x: visible|hidden|scroll|auto|no-display|no-content;
值 描述 测试
visible 不裁剪内容,可能会显示在内容框之外。 测试
hidden 裁剪内容 - 不提供滚动机制。 测试
scroll 裁剪内容 - 提供滚动机制。 测试
auto 如果溢出框,则应该提供滚动机制。 测试
no-display 如果内容不适合内容框,则删除整个框。 测试
no-content 如果内容不适合内容框,则隐藏整个内容。 测试
?
轮播图
swiper(.js).com
表格
[行] [列]
?
table身上的属性
table身上的属性:
border:表格边框 cellspacing:单元格间的间距
cellpadding:单元格的内容与其边框的内边距
bgcolor:表格的背景颜色 background:表格的背景图片
width:表格宽度 height:表格高度
border-collaspe:collaspe:边框合并,不叠加 cellspacing:0:边框合并,但合并之后的边框宽度等于 前两个边框宽度之和
caption:表格标题
background:表格背景图
cellspacing:单元格之间的间隙宽度
align:表格的水平对齐方式,通常是left,center,right
表格的标题
<caption align="水平对齐方式" valign="标题与表格的相对位置"></caption>
单元格【tr】【td】
width:单元格宽度height:单元格高度
align:单元格内文本的对齐方式,通常是左,中,右 left,center,right
valign:单元格内文本的对齐方式,通常是上,中,下 top,middle,bottom
nowrap:在为设置单元格宽度时,当文本长度宽于单元格宽度,将要换行时,该标签会使其不换行
?
<tr align="center" valign="bottom">
<td align="center" nowrap>手机空中免费充值</td>
<td width="100px">IP卡</td>
<td width="100px" bgcolor="#006400" valign="top">网游</td>
</tr>
表格的跨行与跨列【td】
rowspan:跨行标签,表示跨了多少行
colspan:跨列标签,表示跨了多少列
表格标签拓展及其属性
thead:定义表格的表头。
tbody:定义表格主体(正文)。
tfoot:定义表格的页脚(脚注或表注)。
colgroup:标签用于对表格中的列进行组合,以便对其进行格式化。
注意:不管thead、tbody、tfoot的代码先后顺序如何,html显示时,始终是先显示thead,再显示tbody,最后显示tfoot。
1、<thead> 内部必须拥有 <tr> 标签!
2、<tfoot> 元素内部必须包含一个或者多个 <tr> 标签。
3、<tbody> 元素内部必须包含一个或者多个 <tr> 标签。
4、必须在 table 元素内部使用这些标签。
5、当不同行间的单元格合并时各单元格所在的行不要加tbody标签。
标题栏
《tr》<th></th>《/tr》 用法和td相似 知识自动将单元格内容以粗体显示
表单控件表单标签
<form action=" " method=" ">
action:表单信息提交的位置;
method:提交的方式
get:地址栏,信息量少,安全性低
post:信息量多,比较安全
1.输入文本【输入框】:
用户名:<input type="text" placeholder="请输入用户名" maxlength="10" value=" " name="username" class="">
placeholder:默认提示文本;
maxlength:规定输入的最大字符数
name:本文本框的名字,与后台进行数据交互用
class:定义本文本框的样式,相当于盒子
placeholder下的缩进
text-indent:2em;缩进
2.输入密码【密码框】:
密码:<input type="password" placeholder="请输入密码" maxlength="10" value=" " name="psw" class="">
3.单选按钮[name的值必须相同]:
请选择你的性别:
<label for="man"> [label实现点什么就选中 ,for中的值和id中的值相同]
男:<input type="radio" name="sex" id="man" checked> //checked默认选项
</label>
<label for="woman">
女:<input type="radio" name="sex" id="woman">
</label>
4.多选按钮[name的值必须相同]:
请选择你喜欢的音乐:
摇滚:<input type="checkbox" checked>
摇滚:<input type="checkbox" checked>
摇滚:<input type="checkbox" checked>
5.下拉列表【下拉框】:
选择你的学历:
<select name="" id="">
<option value="">学士</option>
<option value="">博士</option>
<option value="">硕士</option>
</select>
6.上传文件:
选择你的照片:
<input type="file">
7.留言文本空间:
<textarea name="" id="" rows="" col="">
</textarea>
8.用户是否允许重新设置textarea大小css属性:
resize: none/both/vertical/horizontal;不允许/上下允许拖动/只能在垂直方向拖动/只能在水平方向 拖动
9.重置按钮:
<input type="reset">
10.提交按钮:
<input type="submit">
11.自定义按钮:
<input type="button" value="按钮">
<button>搜索</button>
12.颜色:
<input type="color">
13.时间日期:
年月:<input type="month">
年周:<input type="week">
时分:<input type="time">
年月日:<input type="date">
年月时分:<input type="datetime-local">
14.验证
<input type="email"> 邮箱验证
<input type="tel" autofocus> 电话
15. autofocus 自动获取焦点
</form>
文本模型
文本换行
使非中日韩文本换行
word-break: break-all ;
文本禁止换行
white-space:nowrap;
单行文本溢出部分以省略号显示
overflow: hidden;(放文本的容器)
text-overflow: ellipsis;
多行文本溢出
音频视频标签
音频标签
<audio src="" controls loop autoplay></audio>
controls 空间向用户显示:
loop 循环播放
autoplay当前页面加载完自动播放
视频标签
<video src="" controls loop autoplay></video>
H5语义化标签
<header>头部</header>
<nav>导航</nav>
<aside>侧导航<aside>
<section>页面中的某一部分</section>
<main>主体</main>
<footer>底部</footer>
meta标记【签】
name="关键字" cantent="内容"
<mate http-equiv="Refresh" content="10";url="跳转路径"> //每10s刷新一次并且跳转到跳转路径知识的文件
bgsound标签
<bgsound src="路径" loop="播放次数">
body属性
对文字操作的标签
列表标签
2. <li>
<li>
</ul>
4. <li>
<li>
</ol>
a标签
<a href="路径 " title="鼠标移入时显示的文字" target=" "(新窗口打开的位置 _self:在本窗口打开;_blank:在新窗口打开;_parent:在当前窗口的父窗口打开链接;_top:在整个浏览器窗口打开) ...
[字符实体]常用的转义字符
显示结果 描述 实体名称
空格
< 小于号
大于号
& 和号
" 引号
' 撇号 (IE不支持)
¢ 分(cent)
£ 镑(pound)
¥ 元(yen)
欧元(euro)
§ 小节
? 版权(copyright)
? 注册商标
? 商标
× 乘号
÷ 除号
选择器
分类
css选择器
1.通用选择器:
*{}//选择所有的标签
2.群组选择器:
E1,E2,E3..{}//选择E1 E2 E3
3.标签选择器
标签名{}
4.类名选择器:
.类名{}
5.后代选择器
.E1 .E2{} //选择E1 的后代E2
6.交叉选择器
标签名.类名{}
7.id选择器
例如 创建id
<div id=“box”></div>
#id名{} //选择页面中id为**的标签
8.伪类选择器:
鼠标移入状态
E:hover{ } E元素选择鼠标移入状态
E:hover .子类{ } 选择e元素下鼠标移入时子类的变化
获取焦点,用于表单的输入
E: focus{
outline: none;
}
9.伪结构选择器:
E:first-child{} 作为子元素的第一个孩子的E标签
E:last-child{} 作为子元素的最后第一个孩子的E标签
E: nth-child(n){} //作为子元素的第n个孩子的E标签
E: nth-last-child(n) 作为子元素的倒数第n个孩子的E标签
E:first-of-type{} 作为子元素的同类型的第一元素
E:last-of-type{} 作为子元素的同类型中的最后一个元素
E: nth-of-type(n) 作为子元素的同类型中的第n个元素
E: nth-last-of-type(n) 作为子元素的同类型中的倒数第n个元素
(n)n可以以为num/even(偶数)/odd(奇数)/3n(3的倍数)
例子:5.15/伪结构选择器
10.伪元素选择器:
::after{} 在元素之后加入一个
::before{
content:“内容之前”;
color:;
} 在元素之前
属于行内元素
::after{} ::before{}伪元素 content:""; 样式必须写
?
11.子类选择器
相邻兄弟选择器
E1+E2{} 选择E1的下一个兄弟元素E2(不能选中上一个兄弟元素)
div.box>a+img a和img统计
子类选择器
E1>E2{} 选择E1的子类元素E2
例子:div.box>div.item{$}*20
div.box>a>img
12.属性选择器
[属性名]{} 选择所有拥有属性为 属性名 的元素
[属性名=“value”]{} 选择拥有属性名的属性 且属性值为value
E[属性名=“value”]{} 选择拥有属性名的属性 且属性值为value的E元素
E[属性名~=“value”]{} 选择拥有属性名的属性 并且属性值一个或者多个,其中一个属性值为value的E元素
E[value^=“1”]{} 选择拥有 value的属性 并且属性值一个或者多个,其中一个属性值以 1 开头的E元素
E[value$=“1”]{} 选择拥有 value的属性 并且属性值一个或者多个,其中一个属性值以 1 结尾的E元素
E[value*=“1”]{} 选择拥有 value的属性 并且属性值一个或者多个,其中一个属性值包含 1 的E元素
例子:属性选择器
选择器的优先级
宗旨:越具体的优先级越高
id (100 ) > class( 10 )> 标签名( 1)
.box .son{ } 10+10=20
abcde优先级(e为个位):
a:行内样式
b:id选择器
c:类名选择器 伪类选择器(:hover) 属性选择器
d:标签选择器 伪元素选择器 (::after)
e:通用选择器有一个
选择有中有一个abcde在其位置+1
移动端布局步骤
?
视口:视觉视口,布局视口,理想视口
em:当前字体的倍率 100px=10em
rem:html字体的倍率
移动端窗口 375*667
html{
font-size:0.5rem;
}
.box{
width: 750rem; //375px=750rem*0.5px ; 100px=1rem
height: 1334rem;
}
未完待续,感谢关注好程序员前端教程分享!
*请认真填写需求信息,我们会在24小时内与您取得联系。