1.模块化概念
模块化是一个语言膨胀的必经之路,它能够帮助开发者拆分和组织代码
无模块化
JavaScript初衷:实现简单的页面交互逻辑,寥寥数语即可。随着web2.0时代的到来,Ajax技术得到广泛应用,jQuery等前端库层出不穷,前端代码日益膨胀。这时候JavaScript作为嵌入式的脚本语言的定位动摇了,JavaScript却没有为组织代码提供任何明显帮助,甚至没有类的概念,JavaScript极其简单的代码组织规范不足以驾驭如此庞大规模的代码
<script src="jquery.js"></script>
<script src="jquery_scroller.js"></script>
<script src="main.js"></script>
<script src="other1.js"></script>
<script src="other2.js"></script>
<script src="other3.js"></script>
- 优点:相比于使用一个js文件,这种多个js文件实现最简单的模块化的思想是进步的
- 缺点:污染全局作用域。 因为每一个模块都是暴露在全局的,简单的使用,会导致全局变量命名冲突,当然,我们也可以使用命名空间的方式来解决。 对于大型项目,各种js很多,开发人员必须手动解决模块和代码库的依赖关系,后期维护成本较高。 依赖关系不明显,不利于维护
模块化
是一种处理复杂系统分解为代码结构更合理,可维护性更高的可管理的模块的方式,在理想状态下我们只需要完成自己部分的核心业务逻辑代码,其他方面的依赖可以通过直接加载被人已经写好模块进行使用即可。在模块化规范形成之前,JS开发者使用Module设计模式来解决JS全局作用域的污染问题。Module模式最初被定义为一种在传统软件工程中为类提供私有和公有封装的方法。在JavaScript中,Module模式使用匿名函数自调用 (闭包)来封装,通过自定义暴露行为来区分私有成员和公有成员
2.模块化的演变过程
面向过程(函数)
优点:直接调用
缺点:变量可能会出现重复造成的污染,并且无法进行结构性分类
命名空间(对象)
优点:变量不会被直接污染,并且易于分类描述内容
缺点:会暴露所有成员,内部状态可以被外部改写
闭包
优点:外部代码无法读取到内部的私有变量,保证了变量不被污染,基本上这种写法就是模块的写法了,但是单纯的这样描述仍然不算完美,并且模块完成后不支持扩展,因此我们在其基础上进行了修改
缺点:内存泄漏
基础闭包
var module = (function(){
var c = 10;
return {
a: function() {},
b: function() {}
}
})();
放大模式
var obj = (function(module){
module.d = function() {}
return module;
})(module);
宽放大模式
var obj = (function(module){
module.d = function() {}
return module;
})(module || {});
3.模块化开发规范
CommonJS规范
CommonJS是服务器端模块的规范,Node.js采用了这个规范。根据CommonJS规范,一个单独的文件就是一个模块。加载模块使用require方法,该方法读取一个文件并执行,最后返回文件内部的exports对象
main.js
const a = require('./a'); // 从a.js中导入默认导出的内容,命名为a,名字可随意修改
const { part1, part2 } = require('./b'); // 从b.js导入普通导出的内容,名字得保持一致
a.js
module.exports = (function(){ // 默认导出,多个会被覆盖,只保留最后一个
return {
a: function() {
console.log('aaa');
}
}
})();
b.js
exports.part1 = function() { // 普通导出,单文件可以有多个普通导出
console.log('part1');
}
exports.part2 = function() {
console.log('part2');
}
CommonJS加载模块是同步的,所以只有加载完成才能执行后面的操作。像Node.js主要用于服务器的编程,加载的模块文件一般都已经存在本地硬盘,所以加载起来比较快,不用考虑异步加载的方式,所以CommonJS规范比较适用。但如果是浏览器环境,要从服务器加载模块,这是就必须采用异步模式。所以就有了AMD、CMD解决方案
AMD规范
AMD(Asynchronous Module Definition)中文名是异步模块定义的意思。它是一个在浏览器端模块化开发的规范。AMD是RequireJS在推广过程中对模块定义的规范化产出,AMD只支持导出对象,其它数据类型需要用对象包裹
- 优点:适合在浏览器环境中异步加载模块。可以并行加载多个模块
- 缺点:提高了开发成本,并且不能按需加载,而是必须提前加载所有的依赖
html文件
<script src="require.js" data-main="main" defer></script>
<!-- defer属性规定是否对脚本执行进行延迟,直到页面加载为止 -->
main.js
// 该文件执行必须依赖require.js文件加载完成,为模块化的入口文件
// []内放入模块地址,后面对应的形参代表模块的名称,名称可以随意,顺序不可打乱
// 当文件地址写扩展名.js时,路径相对于当前js被引入的页面,当不写扩展名时,路径相对于当前js
// 文件的导入是异步过程,加载完成后执行回调,代码
require(['./a', './b'], function(a, b) {
console.log(a);
console.log(b);
a.play();
b.run();
}); // 不导出,只导入模块,只会在主文件中使用
a.js
define({
play: function() {
console.log('模块a的play打印');
}
}); // 无依赖模块定义,导出
b.js
define(['./a'], function(a) {
return {
run: function() {
a.play();
}
}
}); // 有依赖模块定义,导出,先导入依赖模块,再导出模块,支持导入多个
CMD规范
CMD是SeaJS在推广过程中对模块定义的规范化产出
- 优点:同样实现了浏览器端的模块化加载,可以按需加载,依赖就近
- 缺点:依赖SPM打包,模块的加载逻辑偏重
//AMD
define(['./a','./b'], function (a, b) {
// 依赖一开始就写好
a.play();
b.run();
});
//CMD
define(function (requie, exports, module) {
// 依赖可以就近书写
var a = require('./a');
a.play();
...
// 软依赖,判断条件是否加载
if (status) {
var b = requie('./b');
b.run();
}
});
AMD和CMD的区别
AMD是RequireJS在推广过程中对模块定义的规范化产出,CMD是SeaJS在推广过程中被广泛认知。RequireJs出自dojo加载器的作者James Burke,SeaJs出自国内前端大师玉伯
- 对于依赖的模块AMD是提前执行,CMD是延迟执行
- AMD推崇依赖前置(在定义模块的时候就要声明其依赖的模块),CMD推崇依赖就近(只有在用到某个模块的时候再去require——按需加载)
- AMD的api默认是一个当多个用,CMD严格的区分推崇职责单一
ES6模块化
html文件
<script src="main.js" type="module"></script>
<!-- type="module"表示开启ES6模块化 -->
main.js
import all from './a.js'; // 从a.js导入默认导出内容命名为all,名字可更改
import { part1, part2 } from './b.js'; // 从b.js导入普通导出的内容,名字得保持一致
a.js
export default { // 默认导出,单个文件只能有一个默认导出
a: function() {
console.log('aaa');
}
}
b.js
export const part1 = function() { // 普通导出,单文件可以有多个普通导出
console.log('part1');
}
export const part2 = function() {
console.log('part2');
}
4.CSS预编译器
CSS缺点
- 语法不够强大,比如无法嵌套书写导致模块化开发中需要书写很多重复的选择器。
- 没有变量和合理的样式复用机制,使得逻辑上相关的属性值必须以字面量的形式重复输出,导致难以维护。
- 没有变量和合理的样式复用机制,使得逻辑上相关的属性值必须以字面量的形式重复输出,导致难以维护
预编译器
-
用一种专门的编程语言,为CSS增加了一些编程的特性,将CSS作为目标生成文件,然后开发者就只要使用这种语言进行编码工作。
-
通俗的说,CSS预处理器用一种专门的编程语言,进行Web页面样式设计,然后再编译成正常的CSS文件,以供项目使用。
-
CSS预处理器为CSS增加一些编程的特性,无需考虑浏览器的兼容性问题。例如你可以在CSS中使用变量、简单的逻辑程序、函数等等在编程语言中的一些基本特性,可以让你的CSS更加简洁、适应性更强、可读性更佳,更易于代码的维护等诸多好处。
-
但是CSS预编译器也存在CSS的文件体积和复杂度不可控、调试难度增加、成本等的问题。
-
sass:2007年诞生,最早也是最成熟的CSS预处理器,拥有ruby社区的支持和compass这一最强大的CSS框架,目前受less影响,已经进化到了全面兼容CSS的scss,sass有两种语法,分别以「 *.sass 」和「 *.scss 」为扩展名
-
less:2009年出现,受sass的影响较大,但又使用CSS的语法,让大部分开发者和设计师更容易上手,在ruby社区之外支持者远超过sass,其缺点是比起sass来,可编程功能不够,不过优点是简单和兼容CSS,反过来也影响了sass演变到了scss的时代,less的扩展名为「 *.less 」
-
stylus:2010年产生,来自Node.js社区,主要用来给Node项目进行CSS预处理支持,在此社区之内有一定支持者,在广泛的意义上人气还完全不如sass和less,stylus扩展名为「 *.styl 」
sass和scss
- sass(英文全称:Syntactically Awesome Stylesheets)是一个最初由Hampton Catlin设计并由Natalie Weizenbaum 开发的层叠样式表语言。
- sass是一个CSS预处理器。sass是CSS扩展语言,可以帮助我们减少CSS重复的代码,节省开发时间。scss是sass3引入新的语法,其语法完全兼容CSS3,并且继承了sass的强大功能。
- scss和sass功能上是完全一致的,唯一不同的是,scss需要使用分号和花括号而不是换行和缩进。
- sass文件中或者文件夹和文件名中禁止出现中文,包括注释!!!
sass语法
body
color: red
font-size: 20px
scss语法
body {
color: red;
font-size: 20px;
}
5.sass的安装
- sass使用基于ruby环境,安装使用sass前需要安装ruby,安装完成ruby -v查看ruby的版本
- ruby安装完成后安装sass(gem包管理器的服务器在国外,所有需要先换掉gem的资源位置,再安装sass)
- gem sources --remove https://rubygems.org/ 移除当前的gem源
- gem sources --add https://gems.ruby-china.com/ 添加国内的源
- gem sources -l 查看源列表,如果只剩国内源则表示正常
- gem install sass 安装sass
- sass -v 查看sass的版本,如果出现版本号则表示sass安装正确
6.sass的使用
sass编写的样式需要编译成css文件后才能使用,sass提供了四种不同的编译风格,编译出来的结果从形式上会不一样
- nested:嵌套缩进的css代码,它是默认值
- expanded:没有缩进的,扩展的css代码
- compact:简洁格式的css代码
- compressed:压缩后的css代码
sass的默认风格编译
- 编译单个文件:sass input.scss output.css
- 监听单个文件:sass --watch input.scss:output.css
- 监听文件目录:sass --watch app:public (二级目录:app/sass:public/stylesheets)
sass设置编译风格
当需要设置编译风格时,加上–style属性和风格名称就可以了
- sass --style compressed input.sass output.css
- sass --style compressed --watch input.scss:output.css
- sass --style compressed --watch app:public
7.sass的基本语法
变量
sass允许使用变量,所有变量以$开头
$blue: #1875e7;
div {
color: $blue;
}
如果变量需要镶嵌在字符串之中,就必须需要写在#{}之中
$side: left;
.rounded {
border-#{$side}-radius: 5px;
}
计算 + - * /
sass允许在代码中使用算式
body {
margin: (14px/2);
top: 50px + 100px;
right: $var * 10%;
}
嵌套
SASS允许选择器嵌套,下面的CSS代码
div h1 {
color: red;
}
可以写成
div {
h1 {
color: red;
}
}
属性也可以嵌套,比如border-color属性,可以写成
p {
border: {
color: red;
}
}
在嵌套的代码块内,可以使用&引用父元素。比如a:hover伪类,可以写成
a {
&:hover {
color: #ffb3ff;
}
}
注释
sass共有两种注释风格
- /* comment */:标准的CSS注释,块注释,会保留到编译后的文件
- // comment:单行注释,只保留在sass源文件中,编译后被省略
继承 @extend
sass允许一个选择器,继承另一个选择器。比如,现有class1:
.class1 {
border: 1px solid #ddd;
}
class2要继承class1,就要使用@extend命令
.class2 {
@extend .class1;
font-size: 120%;
}
mixin
mixin是可以重用的代码块,使用@mixin命令,定义一个代码块
@mixin left {
float: left;
margin-left: 10px;
}
使用@include命令,调用这个mixin
div {
@include left;
}
mixin的强大之处,在于可以指定参数和缺省值
@mixin left($value: 10px) {
float: left;
margin-right: $value;
}
使用的时候,根据需要加入参数
div {
@include left(20px);
}
下面是一个mixin的实例,用来生成浏览器前缀
@mixin rounded($vert, $horz, $radius: 10px) {
border-#{$vert}-#{$horz}-radius: $radius;
-moz-border-radius-#{$vert}#{$horz}: $radius;
-webkit-border-#{$vert}-#{$horz}-radius: $radius;
}
使用的时候,可以像下面这样调用
#navbar li {
@include rounded(top, left);
}
#footer {
@include rounded(top, left, 5px);
}
插入文件
@import命令,用来插入外部文件
@import "path/filename.scss";
如果插入的是.css文件,则等同于css的import命令
@import "path/filename.css";
8.sass的高级用法
条件语句
@if可以用来判断
p {
@if 1 + 1 == 2 {
border: 1px solid;
}
@if 5 < 3 {
border: 2px dotted;
}
}
配套的还有@else命令
div {
@if 3 > 1 {
color: #000000;
} @else {
color: #ffffff;
}
}
循环语句
sass支持for循环
@for $i from 1 to 10 {
.border-#{$i} {
border: #{$i}px solid blue;
}
}
sass也支持while循环
$i: 6;
@while $i > 0 {
.item-#{$i} {
width: 2em * $i;
}
$i: $i - 1;
}
each命令,作用与for类似
@each $member in a, b, c, d {
.#{$member} {
background-image: url("/image/#{$member}.jpg");
}
}
自定义函数
sass允许用户编写自己的函数
@function double($n) {
@return $n * 2;
}
#sidebar {
width: double(5px);
}