大前端的技术原理和变迁史

本文适合前端新手入门,阅读人群最好是前端新手或者后台开发人员,由于我不敢保证对前端老司机有太多收获。javascript

经过阅读本文,你将会大体了解前端这些年发生的事情,以及一些前端当前主流技术的简单原理介绍。全部涉及的内容,都是尽量的让你捅破这层窗户纸,知道其大体的玩法。css

目录

  1. 前端技术发展轨迹
    • 角色架构发展史
    • 前端技术发展史
    • ECMAScript语法发展史
    • Ajax技术发展史
  2. 当前主流技术原理介绍
    • SPA 单页面应用原理
    • Node.js 服务器端JS运行原理
    • SSR 服务器端渲染原理
    • Vue MVVM原理
    • Webpack 打包原理
    • Sass CSS编译原理
  3. 一些新技术探索
    • TypeScript
    • PWA
    • GraphQL
    • Flutter
  4. 两个流行的概念解答
    • 什么是大前端
    • 什么是前端工程化

1、前端技术发展轨迹

本节知识点:html

  • 角色架构发展史
  • 前端技术发展史
  • ECMAScript语法发展史
  • Ajax技术发展史

1.1 角色架构发展史

https://user-gold-cdn.xitu.io/2018/7/27/164daee36340b5d2?w=1730&h=1186&f=png&s=244723

Web1.0 到 Web2.0过渡的标志,就是Ajax的出现(2005年)。前端

1.2 前端技术发展史

https://user-gold-cdn.xitu.io/2018/7/27/164daee3634db870?w=1584&h=1268&f=png&s=313531

1.3 ECMAScript语法发展史

1.4 Ajax技术发展史

AJAXAsynchronous JavaScript and XML(异步的 JavaScript 与 XML 技术)。vue

STEP1:XMLHttpRequest 原生对象

var request = new XMLHttpRequest();
request.open('GET', '/my/url', true);

request.onload = function() {
  if (request.status >= 200 && request.status < 400) {
    // Success!
    var data = JSON.parse(request.responseText);
  } else {
    // We reached our target server, but it returned an error

  }
};

request.onerror = function() {
  // There was a connection error of some sort
};

request.send();
复制代码

STEP2:$.ajax 操做

$.ajax({
    type: 'GET',
    url: '/my/url',
    data: data,
    success : function(result){
        //TODO RESULT
    }
});
复制代码

STEP3:Promise 操做

let getData = function (url) {
    return new Promsie(function (resolve, reject) {
        $.ajax({
            type: 'GET',
            url: url,
            success: function (data) {
                resolve(data);         
            },
            error: function (err) {
                reject(err);
            }
        });
    });
};

var data = getData('/my/url').then(function (data) {
     //TODO DATA
});
复制代码

STEP4:生成器 Gererator

let it = null;
let ajax = function(url,data){
    $.ajax({
       type: 'GET',
       url: url,
       data: data || {},
       success : function(result){
			it.next(result);
       }
    });
};

function *getData(){
    var data = yield ajax('/my/url');
    console.log('data=',data);
};

it = getData();
it.next(); 
复制代码

STEP5:Async/Await 高级操做

let ajax = function(url,data){
    return $.ajax({
       type: 'GET',
       url: url,
       data: data || {}
    });
};

async function getData(){
    var data = await ajax('/my/url');
    console.log('data=',data);
};

getData();
复制代码

Ajax的相关背景资料:java

1999年,微软公司发布IE浏览器5.0版,第一次引入新功能:容许JavaScript脚本向服务器发起HTTP请求。这个功能当时并无引发注意,直到2004年Gmail发布和2005年Google Map发布,才引发普遍重视。node

2005年2月,AJAX这个词第一次正式提出,指围绕这个功能进行开发的一整套作法。今后,AJAX成为脚本发起HTTP通讯的代名词,W3C也在2006年发布了它的国际标准。jquery

2、当前主流技术原理介绍

本节知识点:webpack

  • SPA 单页面应用原理
  • Node.js 服务器端JS运行原理
  • SSR 服务器端渲染原理
  • Vue MVVM原理
  • Webpack 打包原理
  • Sass CSS开发原理

2.1 SPA 单页面应用原理

什么是SPA? SPA 即单页面,就是页面总体不刷新,不一样的页面只改变局部的内容的一种实现方式。git

一个完整的URI有如下几部分组成:

scheme:[//[user[:password]@]host[:port]][/path][?query][#fragment]
复制代码

浏览器的URL也遵循以上规则,而对于以上规则中,只有 # 后面的 fragment 发生改变时,页面不会从新请求,其它参数变化,均会引发页面的从新请求,而在Js中偏偏还有事件 window.onhashchange 能监听到 fragment 的变化,因而就利用这个原理来达到一个修改局部内容的操做。

#fragment 部分就是对应到Js中的 location.hash 的值。

直接上代码描述:

<!DOCTYPE html>
    <head>
        <script type="text/javascript"> window.onhashchange = function(){ var page = location.hash; if(page === '#home'){ document.getElementById('main').innerHTML = '这是首页'; return; }; if(page === '#help'){ document.getElementById('main').innerHTML = '这是帮助页面'; return; }; document.getElementById('main').innerHTML = '404'; } </script>
    </head>
    <body>
        <header>
        	<a href="#home">首页</a>
            <a href="#help">帮助</a>
        </header>
        <article id="main"></article>
    </body>
</html>
复制代码

以上代码直接在HTML上面有2个连接,点击后在页面的部分区域直接显示这2个连接对应的不一样的页面内容。

2.2 Node.js 服务器端JS运行原理

服务器上如何应用Js呢?这句话能够理解为:在非网页状况下如何运行Js程序(或者命令行如何运行)。

与Java在服务器上运行须要按照JDK同样,Js要运行也须要安装Node环境,安装之后就能够运行了,具体的能够对照着Java程序来解释说明。

2.3 SSR 服务器端渲染原理

用过Java的人必定对 FreeMarker 不陌生,其工做原理:

对于Node.js来讲也是同样,只是使用的框架不是FreeMarker罢了!究其原理无非都是:

Template + Data = Output

只是如今前端说的这个SSR,其实就是指后台渲染好数据,直接返回到浏览器,浏览器就直接显示了,下面咱们作一个对比,用来讲明传统的AJax操做和SSR之间的区别。

使用Ajax操做数据渲染到页面

<!DOCTYPE html>
    <head>
        <script type="text/javascript" src="lib/jquery.min.js"></script>
        <script type="text/javascript"> /** * 使用jQuery将后台接口返回的数据显示到页面上 */ function renderData(){ $.post(url,param,function(result){ //假设返回的是是一个List,咱们追加到页面的ul中 $.each(result,function(i,d){ $('#list').append('<li>' + d.name + '</li>'); }) }); }; renderData(); </script>
    </head>
    <body>
        <ul id="list"></ul>
    </body>
</html>
复制代码

主要流程以下:

  • 用户地址栏输入URL
  • 浏览器使用HTTP协议从后台获取资源
  • 浏览器解析并渲染HTML页面呈现到浏览器上,同时异步执行Ajax操做
  • 浏览器发送Ajax请求后台接口
  • 浏览器获取到数据后,执行回调函数,将内容动态追加到页面上

使用SSR技术显示页面

下面示例咱们以Vue框架后台直出方案为例:

const Vue = require('vue')
const server = require('express')()
const renderer = require('vue-server-renderer').createRenderer()

server.get('*', (req, res) => {
  //vue对象包含了template+data
  const app = new Vue({
    data: {
        list: [{
            name : 'lilei'
        },{
            name : 'hanmeimei'
        }]
    },
    template: `<ul><li v-for="item in list">{{item.name}}</li></ul>`
  })

  //将vue对象传入最终返回output结果html
  //再将html经过reponse对象返回给前端浏览器
  renderer.renderToString(app, (err, html) => {
    res.end(` <!DOCTYPE html> <html> <body>${html}</body> </html> `)
  })
})

server.listen(8080)
复制代码

主要流程以下:

  • 用户地址栏输入URL
  • 浏览器使用HTTP协议从后台获取资源
  • 浏览器解析并渲染HTML页面呈现到浏览器上

若是有人对这个vue示例的完整构建流程感兴趣,能够按照以下流程快速搭建环境并容许起来:

  • 下载并安装Nodejs:https://nodejs.org/zh-cn/download/
  • 新建一个目录,打开命令窗口,切换到当前目录
  • 执行命令:npm init,一直回车进行下一步,快速建立一个node工程
  • 安装所须要的js包:npm i vue express vue-server-renderer --save-dev
  • 新建文件:vi index.js,将上面的vue代码片断所有粘贴进去
  • 执行文件启动Node程序:node index.js
  • 打开浏览器访问:http://localhost:8080(注意端口号不要跟本地已有程序冲突)

2.4 Vue MVVM原理

什么是MVC?什么是MVVM?

经过上图能够清晰的看到,MVVM相比MVC来讲,缺乏了 Controller 一层,传统 Controller 作的事情就是处理一堆复杂的逻辑,而后将数据输出到 View 上面。那么如今缺乏了这一层之后,ViewViewModel 之间如何进行双向自动关联数据绑定的呢?

也就是说,页面上数据发生变化,Js中的数据如何跟着也变化;相反,Js中数据变化了,页面如何自动跟着变化?

举个例子来进一步阐明这个问题,下面的效果如何实现?

页面数据发生变化如何通知到JS

经过给页面元素添加 onchange 或者 oninput 事件,在事件中获取表单的值,而后赋值给Js对应的对象上便可。

好比:示例中的输入框就能够添加oninput事件

<input type="text" oninput="evtInput" />
复制代码

而后在js中定义这个函数执行相关赋值操做就能够:

function evtInput(){
    vue.name = this.value;
}
复制代码

JS数据变化如何通知到页面

JavaScript原生有个方法 Object.defineProperty() ,这个方法能够从新设置一个js对象中某个元素的一些属性,同时提供了 getset 方法,容许用户对元素进行从新赋值和取值操做。

简单分析一下代码:

正式因为咱们能够经过拦截一个属性的 set 方法。因此,咱们就能够在 set 方法中讲获取到的新值赋值给页面元素就能够了。

Object.defineProperty(data,'name',{
    set : function(v){
		document.getElementById('input').value = v;
	}
});
复制代码

2.5 Webpack 打包原理

前端为何要打包?

那么前端为何要进行打包呢?前端代码不是直接就能运行到浏览器么,还打包干吗?

要搞清楚这个问题,那么咱们就以java为例可能比较恰当,好比:java的工程目录中有一个源码目录:src

它是用来存放java源码的,可是java实际编译后确定就没有src这个目录了对吧?

那么 src 源码的做用是什么呢?就是用来更好的归类整理咱们的源代码,它有多是跟咱们实际运行的代码结构彻底不同,由于实际运行的是机器能读懂的,而源码是给人看的。

因此前端也同样,因为当前阶段,前端的业务逻辑也变的很是复杂,再不是传统意义上的一个 html、一个js、一个css就能搞定的。因此咱们要分模块,分目录存放源码,最终经过打包再组装成浏览器能够读懂的代码和目录结构。

好比:咱们经过vue的脚手架建立一个vue的webpack项目,看看它的默认的源码目录(src)的树结构:

src
├── App.vue
├── assets
│   └── logo.png
├── components
│   └── HelloWorld.vue
├── main.js
└── router
    └── index.js
复制代码

src中除了有3个子目录 assets components router ,还有2个 .vue 结尾的文件。因此,这种目录结构和文件在浏览器中确定是没法运行的,若是咱们要运行,就必须对它进行编译,翻译成浏览器能读懂的html/js/css文件才行。

当咱们打包之后,看到项目的dist目录下就有了编译之后的,浏览器可执行的代码结构:

dist
├── index.html
└── static
    ├── css
    │   ├── app.30790115300ab27614ce176899523b62.css
    │   └── app.30790115300ab27614ce176899523b62.css.map
    └── js
        ├── app.b22ce679862c47a75225.js
        ├── app.b22ce679862c47a75225.js.map
        ├── manifest.2ae2e69a05c33dfc65f8.js
        ├── manifest.2ae2e69a05c33dfc65f8.js.map
        ├── vendor.7fed9fa7b7ba482410b7.js
        └── vendor.7fed9fa7b7ba482410b7.js.map
复制代码

因此,当前前端代码逻辑复杂,各类框架横行的年代,源码都是不能直接在浏览器访问的,都须要借助打包工具,如:gulp、webpack这些工具进行打包翻译,才能获得真正的可执行的文件。

Webpack 打包原理

一句话归纳Webpack的本质:

webpack 是一个打包模块化js的工具,能够经过Loader转换文件,经过Plugin扩展功能。

Webpack打包的简易示意图:

2.6 Sass CSS编译原理

什么是Sass?就是一种能提升CSS开发效率的工具。

其编译原理是:

实际在项目中怎么用Sass呢?

前面在说Webpack的时候说过了,如今前端技术离开打包工具是不能直接运行 ,相似于这种Sass文件也同样。因此,咱们只须要在Webpack中增长Sass文件的Loader,这样在代码编译环境,就能自动把Sass文件转换为Css文件,最后引入到Html中的是Css文件,这样页面就能正常渲染了。

Sass有什么好处?

test.scss文件:

$color-red: #00ff00;
$color-white: #ffffff;

#main p {
  color: $color-red;
  width: 97%;

  .redbox {
    background-color: $color-red;
    color: $color-white;
  }
}
复制代码

若是用传统的css写:

#main p {
  color: #00ff00;
  width: 97%; 
}

#main p .redbox {
    background-color: #00ff00;
    color: #ffffff; 
}
复制代码

3、一些新技术探索

本节知识点:

  • TypeScript
  • PWA
  • GraphQL
  • Flutter

3.1 TypeScript

什么是TypeScript?

TypeScript 是微软开源发布的JavaScript类型的超集,它能够编译成纯JavaScript。

它是一个Js框架,能够用来开发前端系统。

之因此说TypeScript是JavaScript的超集,就意味着TypeScript在JavaScript的语法基础上,又扩展了更多语法,使得开发更加方便。

接下来咱们看看TypeScript的相比JavaScript额外增长的部分:

学Java的同窗不要惊慌,我的看来TypeScript就是把Java用JavaScript从新实现了一遍!

一、强数据类型

//Boolean类型
let isDone: boolean = false;

//数字类型
let decLiteral: number = 6;

//字符串
let name: string = "bob";

//数组
let list: Array<number> = [1, 2, 3];

//函数定义
function add(x: number, y: number): number {
    return x + y;
}
复制代码

二、接口

interface LabelledValue {
  label: string;
}

function printLabel(labelledObj: LabelledValue) {
  console.log(labelledObj.label);
}

let myObj = {size: 10, label: "Size 10 Object"};
printLabel(myObj);
复制代码

这里的接口与Java中的接口不同,TypeScript中的接口仅仅是对参数的一种契约约定,即:参数必须是接口定义的结构和参数名等。

三、类

//定义类
class Greeter {
    greeting: string;
    constructor(message: string) {
        this.greeting = message;
    }
    greet() {
        return "Hello, " + this.greeting;
    }
}

let greeter = new Greeter("world");

//类的继承
class Animal {
    move(distanceInMeters: number = 0) {
        console.log(`Animal moved ${distanceInMeters}m.`);
    }
}

class Dog extends Animal {
    bark() {
        console.log('Woof! Woof!');
    }
}

//共有、私有变量、方法
class Animal {
    private id: number; //仅类内部访问,实例没法访问
    public name: string;//类内部和实例均可以访问
    protected pid: number;//仅类内部和子类能够访问
    
    public constructor(theName: string) { this.name = theName; }
    public move(distanceInMeters: number) {
        console.log(`${this.name} moved ${distanceInMeters}m.`);
    }
}
复制代码

四、泛型

function identity<T>(arg: T): T {
    return arg;
}
复制代码

五、枚举

enum Direction {
    Up = 1,
    Down,
    Left,
    Right
}
复制代码

3.2 PWA

什么是PWA?

Progressive Web App, 简称 PWA,是渐进式提高 Web App 的体验的一种新方法,能给用户原生应用的体验。

PWA 本质上是 Web App,就是运行在手机上的App不是纯Native的,而是不少页面都是网页。

Web App ,咱们都知道就是App中有网页嘛!可是 Progressive 渐进式 又该怎么理解?

我的理解,渐进式 的意思就是:按部就班的发展或者改造

PWA更直白的意思就是:

第一步:咱们如今要开发一个App,可是开发纯原生App的维护和扩展成本过高,因此咱们须要增长一些网页进去,毕竟网页好维护嘛!(这部分其实就是混合类App,也叫:Hybrid App)

第二步:可是咱们App中增长网页不能太暴力,太暴力容易让用户以为很不天然,因此须要使用按部就班的方式进行,那么如何按部就班的进行才能让用户的体验达到极致(也就是几乎看不出来某一个页面是一个网页!)

第三步:咱们就须要增长一些策略,先保证用户体验,好比:为了保证安全网页所有使用HTTPS、使用离线缓存来减小用户打开页面的等待时间等等。

因此,PWA就是以按部就班的方式,提高混合类APP的用户体验,而影响体验最大的问题就是页面加载,因此PWA最核心的技术就是:离线缓存 ,离线缓存你们广泛采用的方案就是:Service Worker

3.3 GraphQL

在了解什么是GraphQL以前,咱们先了解一下什么是SQL?

什么是SQL?

Structured Query Language,一种结构化的查询语言,它是关系式数据库管理系统的标准语言。

说白了,SQL是一种特定的语法,也能够称之为是数据库管理的API,操做数据库必须经过这个语法进行。

什么是GraphQL呢?

一种用于API的查询语言

GraphQL 既是一种用于 API 的查询语言也是一个知足你数据查询的运行时。 GraphQL 对你的 API 中的数据提供了一套易于理解的完整描述,使得客户端可以准确地得到它须要的数据,并且没有任何冗余,也让 API 更容易地随着时间推移而演进,还能用于构建强大的开发者工具。

用一组图来讲明下:

那你又要说了?没有GraphQL我如今客户端和后台之间不照样能够交互吗?

没错!你是这么交互的:

因此,不是说离开GraphQL咱们就不能活,而是它给咱们提供了一种全新的API查询交互方式,使得客户端和后台的接口通讯更加高效罢了。

那么GraphQL相比传统的接口请求,都有哪些优点呢?

一、所见即所得

//查询条件
{
    user(uid:1) {
        uid
        name
    }
}

//返回结果
{
  "data": {
    "user": {
      "uid": "1",
      "name": "xxx"
    }
  }
}
复制代码

传统的API是什么样子?

//查询条件
{
    uid : 1
}

//返回结果
{
  "data": {
    "user": {
      "uid": "1",
      "name": "xxx"
    }
  }
}
复制代码

二、减小网络请求次数

只须要一次网络请求,就能得到资源和子资源的数据(例如,上文中文章的评论信息)。

//查询条件
{
    article(aid:1) {
        title
        content
        author {
            uid
            name
        }
    },
    comment {
        content,
        author {
            uid
            name
        }
    }
}
复制代码

传统的查询,通常先查询Article,再查询Comment。固然你能够一次性去查,让后台一次性返回来,可是通常没有后台会给你设计这样的一个API:getArticleAndComment,如何按照Restful的接口标准,你应该须要查询2次。

三、参数类型强校验

GraphQL规定了一套数据类型,这就保证接口查询的时候,字段类型就被明肯定义,而传统的接口通常很难保证查询参数的类型。

好比,如下就是GraphQL的语法定义:

type Starship {
  id: ID!
  name: String!
  length(unit: LengthUnit = METER): Float
}
复制代码

3.4 Flutter

什么是Flutter?

Flutter是谷歌的移动UI框架,能够快速在iOS和Android上构建高质量的原生用户界面。

下图就是使用Flutter开发一款App的样子:

相似于 React Native 框架同样,Flutter也是能够调用一些App系统级的命令,能让你快速开发一款混合类App(Hybrid App)。

有兴趣的能够去官网学习:Flutter中文网

4、两个流行的概念解答

本节知识点:

  • 什么是大前端
  • 什么是前端工程化

4.1 什么是大前端

本文的标题是 大前端的技术原理和演变史,如今给你们解答一下,什么是:大前端

大前端 就是相比 前端 而言变大了,那么到底传统意义的前端指什么?变成大前端又有哪些东西变大了呢?

传统的意义的前端指是什么呢?

传统的前端就是指直接面对客户的应用或者系统,好比:网页、手机App。

而开发网页、搞iOS和Android开发的程序员均可以称之为前端工程师。

只是传统意义来讲,前端工程师仅仅指网页开发的人,而iOS和Android开发的通常指客户端开发人员,或者归结到软件工程师岗位去了。

那么大前端又指什么呢?其实通过各类资料考证,并无这么一个明确的定义,只是随着技术的进步,你们有了一种默认的约定,大前端之因此称之为大前端,主要体如今如下一些方面:

一、大前端 - 先后端分离

随着先后端职责和技术框架的分离发展,产品对前端的要求愈来愈高,用户对前端的期待愈来愈高,前端技术发展愈来愈快,致使前端这个岗位并无像JSP时代那种画画页面就完事了。这部分体现的是前端的要求更高,责任越大了。

二、大前端 - Node全栈

先后端分离后,前端要独立完成一个事情是不行的,由于缺乏后台的支持。可是随着Node的出现,前端能够不用依赖后台人员,也不用学习新的后台语言,就能够轻松搞定后台的这部分事情。这样,面对一些小的系统,前端工程师就能够搞定整个系统。这部分体现了前端的全面性和全栈性。

三、大前端 - 应对各类端

传统的前端工程师,通常指网页开发工程师,网站通常指运行在PC浏览器,慢慢的也要运行在手机上。可是,随着移动互联网的发展,忽然冒出来更多的移动设备,好比:手机分为Android手机和苹果手机、智能手表、VR/AR技术支撑的可穿戴设备、眼睛、头盔、车载系统、智能电视系统等等。而这些设备都须要前端的支撑,这时候对前端的技术要求、能力要求就更高。这部分体现了前端的涉猎范围变大。

四、大前端 - 微应用

当微信小程序出来之后,你们第一感受是前端又能够火一把啦,不须要后台、不须要服务端,只须要在微信平台上开发网页就能够发布上线了。

而近期又有国内多个手机厂家联合推出 快应用 , 跟小程序差很少,只是经过简单的前端开发发布之后,用户不须要安装应用就能够直接在相似于小米、vivo、oppo等手机上打开这样的应用。

相似于这些微应用,免后台、免安装的形式出现,也促使了前端这个行业也将涉及到这样的新型领域中,一块儿推进技术的进步。这部分体现了前端是时代发展的幸运儿。

综上所述,咱们能够获得一个大体的定义:

大前端指前端涉猎范围愈来愈广、涉及的端愈来愈多、技术要求愈来愈高、影响范围愈来愈大的一种体现。

4.2 什么是前端工程化

前端工程化的定义:

前端工程化是根据业务特色,将前端开发流程规范化,标准化,它包括了开发流程,技术选型,代码规范,构建发布等,用于提高前端工程师的开发效率和代码质量,最终交付一个稳定性高、扩展性好、易于维护的系统的过程。

通常状况下,一个符合前端工程化要求的方案应该包含如下要素:

  • 开发规范
  • 模块化开发
  • 组件化开发
  • 组件仓库
  • 性能优化
  • 部署
  • 开发流程
  • 开发工具

另外,咱们再谈到工程化,不能只想着前端工程化,而应该站在整个系统考虑如何进行工程化,也就是说对于一整个项目,咱们谈到工程化应该考虑哪些因素呢?

一个系统的工程化建设,应该包含如下因素:

  • 目标,搞清楚目标群体,并持续为他们作好最优质的服务;
  • 边界,与别的系统划清界限,同时作好接口,保证自身系统职责定位清晰的同时,管理好依赖系统,增长自身健壮性;
  • 壁垒,自身平台建设,其中就包含了前端工程化建设,以及后台工程化建设,还有项目总体建设等诸多因素。

参考资料

相关文章
相关标签/搜索