前端发展演进
工程化即系统化,模块化,规范化的过程。如果说计算机科学要解决的是系统的某个具体问题,或者更通俗点说是面向编码的,那么工程化要解决的是如何提高整个系统生产效率。
# 前端发展历史
后端为主的传统时代
不分前后端,页面由JSP,ASP,PHP等服务端生成,浏览器负责展示。比如Servlet和JSP
后来后端再次发展,后端服务规范化,分层化,出现了许多优秀的架构。比如Strusts,SpringMVC等。
前后端分离为主的时代
随着ajax技术发展,前后端开始分离,前端负责页面渲染,输出和输入信息,后端负责逻辑处理,两者之间通过前后端接口的方式沟通。
后来前端发展,前端出现了AngularJS、React、Vue等大量前端框架,使得前端组织结构更加模块化,规范化。
全栈时代
Node.js的兴起,带来了JavaScript全栈化开发的模式。
# 模块化
简单来说,模块化就是将一个大文件拆分成相互依赖的小文件,再进行统一的拼装和加载。这样便于多人协作
# JS模块化
JavaScript社区制定了一些模块加载方案,如CommonJS、AMD和CMD等,某些框架也会有自己模块系统,比如Angular1.x。
现在随着ES6的出现,使用大一统的ES6规范,基本上可以取代现有的CommonJS和AMD规范。
# CSS模块化
SASS,LESS等CSS预处理的出现实现了CSS文件的模块化。但需要注意CSS全局污染问题。
# 资源模块化
除了JS,CSS之外,其他资源,比如图片,字体,vue单文件等也应该模块化。利用Webpack的强大功能即可解决。
# 组件化
首先,组件化≠模块化。模块化只是在文件层面上,对代码或资源的拆分;而组件化是在设计层面上,对UI(用户界面)的拆分。
从UI拆分下来的每个包含模板(HTML)+样式(CSS)+逻辑(JS)功能完备的结构单元,称之为组件。
目前市面上的组件化框架很多,主要的有Vue (opens new window)、React (opens new window)、Angular (opens new window)。
# 规范化
通过遵守统一规范,能让代码更好维护。
某些规范化,比如:
- 目录结构的制定
- 编码规范
- 前后端接口规范
- 文档规范
- 组件管理
- Git分支管理
- Commit描述规范
- ...
其中编码规范,就有ESLint (opens new window)和StyleLint等强制措施。
# 自动化
更多具体的内容,可参考传送门1 (opens new window),传送门2 (opens new window)。
# NPM
NPM即Node Package Manager,是NodeJS的包管理和分发工具。而NodeJS简单来说就是运行在服务端的JavaScript的运行环境。
NPM是随着NodeJS一起安装的,具体安装流程可参考传送门 (opens new window)。
NPM的主要应用场景有以下几种:
- 允许用户从NPM服务器下载别人编写的第三方包到本地使用。
- 允许用户从NPM服务器下载并安装别人编写的命令行程序到本地使用。
- 允许用户将自己编写的包或命令行程序上传到NPM服务器供别人使用。
以Java开发者视角来看NPM的话,它相当于Maven。
以Python开发者视角来看NPM的话,它相当于Conda。
# ES6
ECMAScript 6.0(简称 ES6)是 JavaScript 语言的下一代标准,已经在 2015 年 6 月正式发布了。它的目标,是使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言。
ECMAScript和JavaScript的关系
简单来说,ECMAScript是浏览器脚本语言的标准,JavaScript是浏览器语言标准的实现之一(另外的 基于ECMAScript标准实现 还有 JScript 和 ActionScript)。日常情况下,两个词是可以互换的。
这有些类似于J2EE的13规范和各大规范实现框架关系。前者一个是规范,后者一个是实现。
ES6与ECMAScrip2015的关系
2011 年,ECMAScript 5.1 版发布后,就开始制定 6.0 版了。因此,ES6 这个词的原意,就是指 JavaScript 语言的下一个版本。但是由于某些原因,直到2015年6月,才正式发布ES6的第一个版本,正式名称为《ECMAScript 2015 标准》(简称 ES2015)。该标准在每年6月正式发布一次。2016年6月,发布《ECMAScript 2016 标准》(简称 ES2016);2017年6月发布《ECMAScript 2017标准》(简称 ES2017)。
因此,ES6 既是一个历史名词,也是一个泛指,含义是 5.1 版以后的 JavaScript 的下一代标准,涵盖了 ES2015、ES2016、ES2017 等等。
ECMAScript历史
- ECMAScript1.0是1997年发布
- ECMAScript2.0是1998年发布
- ECMAScript3.0是1999年发布,在业界得到广泛支持,成为通行标准,奠定了 JavaScript 语言的基本语法。初学者刚开始学习JavaScript,其实就是在学习3.0版本
- ECMAScript 4.0 由于某些原因发布失败
- ECMAScript 5.0 是2009年发布,也就是ES5
- ECMAScript 6.0 是2015年发布,之后每年6月一个小版本的跨越。2016年发布ECMAScript 6.1(ES2016),以此类推
Babel转码器
Babel (opens new window) 是一个广泛使用的 ES6 转码器,可以将 ES6 代码转为 ES5 代码,从而在现有环境执行。主要现在浏览器主要支持ES5语法,为保证代码能够顺利运行,我们需要用转换器将ES6代码转化为浏览器支持的ES5语法。
ES6的内容也挺多的,不是一时半会就能学习和整理完的,但是先要对其有基本的了解,遇到不懂可以边查边了解。在未来再进行基本的内容学习和整理。
具体更多内容,可参考传送门 (opens new window)。
# Webpack
Webpack是一个流行的前端项目构建工具(打包工具),提供友好的模块化支持,代码压缩混淆,处理js兼容,性能优化等功能。目前大多数前端项目都是基于Webpack打包构建的。
具体更多内容,可参考传送门1 (opens new window),传送门2 (opens new window)。
# Webpack的基本使用
通过一个简单列表隔行变色项目,来介绍Webpack。
创建一个空白目录,通过npm init -y
来初始化包管理文件package.json。然后运行npm i jquery -S
命令安装JQuery,然后通过ES6的模块化方式,实现隔行变色的效果。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
<script src="./index.js"></script>
</head>
<body>
<ul>
<li>这是第1个li</li>
<li>这是第2个li</li>
<li>这是第3个li</li>
<li>这是第4个li</li>
<li>这是第5个li</li>
<li>这是第6个li</li>
<li>这是第7个li</li>
<li>这是第8个li</li>
<li>这是第9个li</li>
</ul>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// index.js
import $ from 'jquery'
$(function(){
$('li:odd').css('backgroundColor', 'blue')
$('li:even').css('backgroundColor', 'lightblue')
})
2
3
4
5
6
7
结果,我们发现,浏览器运行时,并没有隔行变色的效果,这是因为在浏览器里不支持ES6的模块化语法,这时候,我们就需要通过webpack打包工具,将我们ES6的模块化语法,转化为浏览器能够兼容的js。
# 安装和配置webpack
接下来,我们通过在项目中安装和配置webpack的方式,解决问题。
首先运行npm i webpack webpack-cli -D
命令,安装webpack。然后在项目的根目录下,创建webpack.config.js的webpack配置文件。在配置文件中添加以下配置:
module.exports = {
mode:'development' //mode 用来指定构建模式
}
2
3
接着在package.json文件中的scripts的节点中,新增dev运行脚本:
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "webpack"
}
2
3
4
最后在终端运行npm run dev
命令,启动webpack进行打包。
> webpack
Hash: ce25f383883ee2f00d11
Version: webpack 4.41.6
Time: 690ms
Built at: 2020-02-20 14:29:44
Asset Size Chunks Chunk Names
main.js 314 KiB main [emitted] main
Entrypoint main = main.js
[./src/index.js] 145 bytes {main} [built]
+ 1 hidden module
2
3
4
5
6
7
8
9
10
11
会发现,webpack将index.js文件打包成main.js文件,将其放置在dist文件夹下,然后将其引入index.html中,再在浏览器运行时,发现隔行变色的效果出现了。
可以见得webpack的将我们基于ES6模块化的代码打包为浏览器可兼容的js脚本。
# webpack配置文件项说明
配置打包的入口和出口
webpakc4.x版本中默认:
- 打包入口文件为
src/index.js
- 打包输出文件为
dist/main.js
如果想要修改打包的入口与出口,只要配置中添加
entry
和output
节点。const path = require('path') module.exports = { mode:'development', //mode 用来指定构建模式 entry:path.join(__dirname,'./src/index.js'), //打包入口文件路径 output:{ path:path.join(__dirname,'./dist'),//输出文件的存放路径 filename:'bundle.js' //输出文件名称 } }
1
2
3
4
5
6
7
8
9
10- 打包入口文件为
配置自动打包功能
运行
npm i webpack-dev-server -D
,安装项目支持自动打包工具。然后修改package.json的script脚本命令:"scripts": { "test": "echo \"Error: no test specified\" && exit 1", "dev": "webpack-dev-server" }
1
2
3
4当执行
npm run dev
后,在浏览器访问http://localhost:8080
地址,即可看到项目结构,点击src,就会访问到index.html文件,当其中内容改变时,可以实时打包更新。还可以配置自动打包相关参数,比如ip地址,端口等。
"scripts": { "test": "echo \"Error: no test specified\" && exit 1", "dev": "webpack-dev-server --open --host 127.0.0.1 --port 8888" }
1
2
3
4- --open 用于打包完成后自动打开浏览器页面
- --host 配置ip地址
- --port 配置端口
配置插件之 html-webpack-plugin
在访问
http://localhost:8080
地址时,发现访问的是项目的结构,我们能通过安装插件的方式,安装生成浏览页面的插件。运行
npm i html-webpack-plugin -D
,然后在webpack.config.js,添加如下配置信息:const path = require('path') const HtmlWebpackPlugin = require('html-webpack-plugin') const htmlPlugin = new HtmlWebpackPlugin({ //创建插件实例对象 template:'./src/index.html', //指定模板文件 filename:'index.html' //指定生成的文件的名称,该文件存在内存中,在目录中不显示 }) module.exports = { mode:'development', //mode 用来指定构建模式 entry:path.join(__dirname,'./src/index.js'), //打包入口文件路径 output:{ path:path.join(__dirname,'./dist'),//输出文件的存放路径 filename:'bundle.js' //输出文件名称 }, plugins:[htmlPlugin] //plugins数组为打包期间用到的插件列表 }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Webpack的加载器
webpack默认只能打包处理.js后缀结尾的模块。其他非.js后缀结果的模块,webpack默认处理不了,需要调用loader加载器才能正常打包,否则会报错。
loader加载器可以协助webpack打包处理特定的文件模块,比如
- css-loader可以打包处理.css相关的文件
- url-loader可以打包处理css中与url路径相关的文件
- vue-loader可以打包处理.vue相关的文件
# 打包处理css文件
安装处理css文件的loader,运行
npm i style-loader css-loader -D
在webpack.config.js的module中添加rules数组节点,添加loader规则
const path = require('path') const HtmlWebpackPlugin = require('html-webpack-plugin') const htmlPlugin = new HtmlWebpackPlugin({ //创建插件实例对象 template:'./src/index.html', //指定模板文件 filename:'index.html' //指定生成的文件的名称,该文件存在内存中,在目录中不显示 }) module.exports = { mode:'development', //mode 用来指定构建模式 entry:path.join(__dirname,'./src/index.js'), //打包入口文件路径 output:{ path:path.join(__dirname,'./dist'),//输出文件的存放路径 filename:'bundle.js' //输出文件名称 }, plugins:[htmlPlugin], //plugins数组为打包期间用到的插件列表 //所有loader加载器的匹配规则 module:{ rules:[ {test:/\.css$/,use:['style-loader','css-loader']} ] } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23注意:
- use数组中指定的loader顺序是固定的
- 多个loader的调用顺序的:从后往前调用
# 打包处理图片文件
安装处理图片文件的loader,运行
npm i url-loader file-loader -D
在webpack.config.js的module中添加rules数组节点,添加loader规则
module.exports = { mode:'development', //mode 用来指定构建模式 entry:path.join(__dirname,'./src/index.js'), //打包入口文件路径 output:{ path:path.join(__dirname,'./dist'),//输出文件的存放路径 filename:'bundle.js' //输出文件名称 }, plugins:[htmlPlugin], //plugins数组为打包期间用到的插件列表 //所有loader加载器的匹配规则 module:{ rules:[ {test:/\.css$/,use:['style-loader','css-loader']}, {test:/\.jpg|png|gif|bmp|ttf|eot|svg|woff|woff2$/,use:'url-loader?limit=16940'} ] } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16注意:
- 当只调用一个加载器时,use可以不为数组
- ?之后的是loader的参数项,limit用来指定图片大小,单位是字节,只有小于limit大小的图片,才会被转为base64图片
# 打包处理js文件中高级语法
安装babel转换器相关的包
npm i babel-loader @babel/core @babel/runtime -D
安装babel语法插件相关的包:
npm i @babel/preset-env @babel/plugin-transform-runtime @babel/plugin-proposal-class-properties -D
在项目根目录中,创建babel配置文件
babel.config.js
,初始化配置:module.exports = { presets:['@babel/preset-env'], plugins:['@babel/plugin-transform-runtime','@babel/plugin-proposal-class-properties'] }
1
2
3
4在webpack.config.js的module中添加rules数组节点,添加loader规则
const path = require('path') const HtmlWebpackPlugin = require('html-webpack-plugin') const htmlPlugin = new HtmlWebpackPlugin({ //创建插件实例对象 template:'./src/index.html', //指定模板文件 filename:'index.html' //指定生成的文件的名称,该文件存在内存中,在目录中不显示 }) module.exports = { mode:'development', //mode 用来指定构建模式 entry:path.join(__dirname,'./src/index.js'), //打包入口文件路径 output:{ path:path.join(__dirname,'./dist'),//输出文件的存放路径 filename:'bundle.js' //输出文件名称 }, plugins:[htmlPlugin], //plugins数组为打包期间用到的插件列表 //所有loader加载器的匹配规则 module:{ rules:[ {test:/\.css$/,use:['style-loader','css-loader']}, {test:/\.jpg|png|gif|bmp|ttf|eot|svg|woff|woff2$/,use:'url-loader?limit=16940'}, {test:/\.js$/,use:'babel-loader',exclude:/node_modules/} ] } }
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注意:
exclude
为排除项,表示babel——loader不需要处理node_modules中的js文件