煦煦

web前端

煦煦的个人站~


JavaScript全栈学习笔记(入门)

参考:阮一峰:JavaScript 全栈工程师培训教程
   课堂练习的操作指导

目录

(目录并不完整,这里只是挑重点放上目录,以便整体把握)

一、环境搭建

  1. 安装Git
  2. 安装Node:
    • 到官网Nodejs或者国内镜像https://npm.taobao.org/mirrors/node,下载安装包。
    • $ node -v:查看安装是否成功;
    • npm:node的模块管理器;
    • $ npm config get registry:查看源;
    • 由于Node的官方模块仓库网速太慢,模块仓库需要切换到阿里的源:
      $ npm config set registry https://registry.npm.taobao.org/
  3. 安装Postman
    Postman是一种网页调试和发送网页http请求的Chrome浏览器插件。
    参考:http://www.cnblogs.com/mafly/p/postman.html

二、前端发展史

历史演变:前后端不分 -> 前后端分离 -> 全栈工程师

前后端不分的时代

  1. 浏览器发出请求,后端收到浏览器请求;
  2. 生成静态页面,发送给浏览器。

后端 MVC 的开发模式

  • Model(模型层):提供/保存数据
  • Controller(控制层):数据处理,实现业务逻辑
  • View(视图层):展示数据,提供用户界面

web2.0

  • Ajax(异步 JavaScript 和 XML):是一种创建快速动态网页的技术,无需刷新网页就能够更新部分网页,通过在后台与服务器进行少量数据交换,使网页实现异步更新。
  • 由于Ajax技术的出现,促成了web2.0的诞生。

前端MVC框架

  前端通过Ajax得到数据,同样有了保存数据(M)、处理数据(C)、生成视图(V)的需求,这导致前端MVC诞生。

  • Backbone.js:将前端分成两个部分
    • Model:管理数据
    • View:数据的展现
    • Controller:前端controller不应有业务逻辑,只需要处理UI逻辑,所以BackBone没有C,只用事件处理UI逻辑。
  • Router
    作用:通过URL切换视图,例如:

MVVM模式

  • Modle
  • View
  • View-Model:简化的Controller,唯一作用就是为View提供处理好的数据,不含其他逻辑。
    本质:view 绑定 view-model,视图与数据模型强耦合。数据的变化实时反映在 view 上,不需要手动处理。

1、SPA(Single-page application)单页应用

  • 读写数据
  • 切换视图
  • 用户交互
    传统架构:

    单页应用架构:

2、Angular

  • Google公司推出的Angular是最流行的MVVM前端框架。
  • 它的风格属于HTML语言的增强,核心概念是双向绑定

Angular的双向绑定:

<div ng-app="">
  <p>
    姓名:<input type="text" ng-model="name" placeholder="在这里输入您的大名"/>
  </p>
  <h1>你好,{ {name} }</h1>
</div>

ng-modle与name绑定,View-Model中,modle为view提供数据。

3、Vue

  • Vue.js是现在很热门的一种前端MVVM框架。
  • 它的基本思想与Angular类似,但是用法更简单,而且引入了响应式编程的概念。

Vue的双向绑定:

HTML代码:
<div id="journal">
	 <input type="text" v-model="message">
	 <div>{ {message} }</div>
</div>
JS代码:
var journal = new Vue({
  el: '#journal',
  data: {
    message: 'Your first entry'
  }
});

v-modle与message绑定。

前后端分离

  • Ajax -> 前端应用兴起
  • 智能手机 -> 多终端支持

REST接口

REST即表述性状态传递(Representational State Transfer)
它是一种针对网络应用的设计和开发方式,可以降低开发的复杂性,提高系统的可伸缩性。
是一组架构约束条件和原则,满足这些约束条件和原则的应用程序或设计就是RESTful。

  • 前后端分离以后,它们之间通过接口通信。
  • 后端暴露出接口,前端消费后端提供的数据。
  • 后端接口一般是REST形式,前后端的通信协议一般是HTTP

Node

它是服务器上的 JavaScript 运行环境。
Node = JavaScript + 操作系统API

Node的意义

  • JavaScript成为服务器脚本语言与PythonRuby一样
  • JavaScript成为唯一的浏览器和服务器都支持的语言
  • 前端工程师可以编写后端程序了

前端开发模式的根本改变

  • Node环境下开发
  • 大量使用服务器端工具
  • 引入持续集成等软件工程的标准流程
    持续集成、持续交付、持续部署:
    • 集成:是指软件个人研发的部分向软件整体部分交付,以便尽早发现个人开发部分的问题;
    • 部署:是代码尽快向可运行的开发/测试节交付,以便尽早测试;
    • 交付:是指研发尽快向客户交付,以便尽早发现生产环境中存在的问题。
  • 开发完成后,编译成浏览器可以运行的脚本,放上CDN(Content Delivery Network)即内容分发网络。

全栈

  • 全栈工程师:
    • 一个人负责开发前端和后端
    • 从数据库到UI的所有开发
  • 全栈技能:
    • 传统前端技能:HTML、JavaScript、CSS
    • 一门后端语言
    • 移动端开发:iOS / Android / HTML5
    • 其他技能:数据库、HTTP 等等

三、React技术栈

  • React 是目前最热门的前端框架。
  • React 是一个用于构建用户界面的JAVASCRIPT库。
  • React 起源于 Facebook 的内部项目,用来架设 Instagram 的网站,并于 2013 年 5 月开源。
  • 现在最好的社区支持和生态圈
  • 大量的第三方工具

React 的优点:

  • 组件模式:代码复用和团队分工
  • 虚拟DOM:性能优势
  • 移动端支持:跨终端

React 的缺点:React非常先进和强大,但是学习和实现成本都不低。

JSX语法

React 使用 JSX 语法,JavaScript 代码中可以写 HTML 代码。

let myTitle = <h1>Hello, world!</h1>;
  • JSX 语法的最外层,只能有一个节点。
  • JSX 语法中可以插入 JavaScript 代码,使用大括号。
let myTitle = <p>{'Hello ' + 'World'}</p>

Babel 转码器

JavaScript 引擎(包括浏览器和 Node)都不认识 JSX,需要首先使用 Babel 转码,然后才能运行。

<script src="react.js"></script>
<script src="react-dom.js"></script>
//React需要加载两个库,前者是 React 的核心库,后者是 React 的 DOM 适配库。
<script src="babel.min.js"></script>
//Babel 用来在浏览器转换 JSX 语法,如果服务器已经转好了,浏览器就不需要加载这个库。
<script type="text/babel">
  // ** Our code goes here! **
</script>

示例:

 <head>
    <meta charset="utf-8">
    <script src="react.js"></script>
    <script src="react-dom.js"></script>
    <script src="babel.min.js"></script>
 </head>
 <body>
    <div id="example"></div>
    <script type="text/babel">
		ReactDOM.render(
  		<span>Hello World!</span>,
  		document.getElementById('example')
		);
    </script>
 </body>

ReactDOM.render方法接受两个参数:一个虚拟 DOM 节点和一个真实 DOM 节点,作用是将虚拟 DOM 挂载到真实 DOM。

React组件

React 允许用户定义自己的组件,插入网页。

React组件语法

class MyTitle extends React.Component {
  render() {
    return <h1>Hello World</h1>;
  }
};

ReactDOM.render(
  <MyTitle/>,
  document.getElementById('example')
);
  • class MyTitle extends React.Component: ES6 语法,表示自定义一个MyTitle类,该类继承了基类React.Component的所有属性和方法。
  • React 规定,自定义组件的第一个字母必须大写,以便与内置的原生类相区分。
  • 每个组件都必须由render方法,定义输出的样式。
  • <MyTitle/>表示生成一个组件类的实例,每个实例一定要有闭合标签,写成<MyTilte></MyTitle>也可。

React组件的参数

组件内部通过this.props对象获取参数:

<body>
<div id="example"></div>
<script type="text/babel">
  class MyTitle extends React.Component {
    render() {
      return <h1 style= { {color: this.props.color} }>Hello World</h1>;
    }
  };

  ReactDOM.render(
    <MyTitle color="red" />,
    document.getElementById('example')
  );
</script>
</body>

React 组件的状态

<body>
<div id="example"></div>
<script type="text/babel">
  class MyTitle extends React.Component {
    constructor(...args) {
      super(...args);
      this.state = {
        name: '访问者'
      };
    }

    handleChange(e) {
      let name = e.target.value;
      this.setState({
        name: name
      });
    }

    render() {
      return <div>
        <input type="text" onChange={this.handleChange.bind(this)} />
        <p>你好,{this.state.name}</p>
      </div>;
    }
  };

  ReactDOM.render(
    <MyTitle/>,
    document.getElementById('example')
  );
</script>
</body>
  • constructor是组件的构造函数,会在创建实例时自动调用。
    ...args表示组件参数,super(...args)是 ES6 规定的写法。
    this.state对象用来存放内部状态,这里是定义初始状态。
  • 组件内部状态:用this.state对象表示
  • 当input元素发生onChange事件时,调用onChange事件指定的监听函数this.handleChange(this表示当前组件)
    .bind(this) 表示:this.handleChange方法内部的this,绑定当前组件。
  • this.setState方法用来重置this.state,每次调用这个方法,就会引发组件的重新渲染。

React 组件的生命周期

React 为组件的不同生命阶段,提供了七个钩子方法

  • componentWillMount:组件渲染前被调用
  • componentDidMount:组件第一次渲染后调用,只执行一次
  • componentWillUpdate:组件更新前被调用(收到新的props或者state但还没有render时)
  • componentDidUpdate:组件更新后调用
  • componentWillUnmount:组件从DOM中移除的时调用。
  • componentWillReceiveProps:组件接受新的参数时调用
  • shouldComponentUpdate

React 组件库

React 的一大优势,就是网上有很多已经写好的组件库,可以使用。
React-Bootstraphttps://react-bootstrap.github.io/
ReCharts:是一个 React 图表组件库。http://recharts.org/
如何使用第三方组件库:

<head>
    <meta charset="utf-8">
    <script src="react-with-addons.min.js"></script>
    <script src="react-dom.js"></script>
    <script src="react-dom-server.min.js"></script>
    <script src="Recharts.min.js"></script>
    <script src="babel.min.js"></script>
</head>
<body>
    <div id="example" style="width: 1000px;height: 800px;"></div>
    <script type="text/babel">
		const {LineChart, Line, XAxis, YAxis, CartesianGrid} = Recharts;
		const data = [
		      {name: 'Page A', uv: 4000, pv: 2400, amt: 2400},
		      {name: 'Page B', uv: 3000, pv: 1398, amt: 2210},
		      {name: 'Page C', uv: 2000, pv: 9800, amt: 2290},
		      {name: 'Page D', uv: 2780, pv: 3908, amt: 2000},
		      {name: 'Page E', uv: 1890, pv: 4800, amt: 2181},
		      {name: 'Page F', uv: 2390, pv: 3800, amt: 2500},
		      {name: 'Page G', uv: 3490, pv: 4300, amt: 2100},
		];

		const TinyLineChart = React.createClass({
			render () {
		  	return (
		  <LineChart width={1000} height={400} data={data}>
		    <XAxis dataKey="name"/>
		    <YAxis/>
		    <CartesianGrid stroke="#eee" strokeDasharray="5 5"/>
		    <Line type="monotone" dataKey="uv" stroke="#8884d8" />
		    <Line type="monotone" dataKey="pv" stroke="#82ca9d" />
		  </LineChart>
		    );
		  }
		})

		ReactDOM.render(
		  <TinyLineChart />,
		  document.getElementById('example')
		);
    </script>
</body> 创建第三方组件用函数:`React.createClass`

React架构—Flux架构

Facebook 提出Flux架构的概念,被认为是React应用的标准架构。 最大特点:数据单向流动。与 MVVM 的数据双向绑定,形成鲜明对比。

核心思想:

  • 不同组件的state,存放在一个外部的、公共的 Store 上面。、
  • 组件订阅 Store 的不同部分。
  • 组件发送(dispatch)动作(action),引发 Store 的更新。

Flux 只是一个概念,有30多种实现。

流行的两个React架构

React 架构的最重要作用:管理 Store 与 View 之间的关系。

  • MobX:响应式(Reactive)管理,state 是可变对象,适合中小型项目
  • Redux:函数式(Functional)管理,state 是不可变对象,适合大型项目

(目前还未去了解这两种方式的架构,有待学习)

React tips

  • JSX语法:JS中可以插入HTML,而插入的HTML中又可以插入JS(用大括号括起来)。
  • React:用JSX语法,自定义组件或者引用第三方组件(虚拟 DOM 节点:代码复用),将其挂载到真实DOM节点ReactDOM.render到页面。
  • React核心思想:View是State的输出;view = f(state),其中f表示函数关系
    React其本质:是将图像界面(GUI)函数化
  • $.getJSON()

使用AJAXHTTP GET请求获取JSON数据。

$(selector).getJSON(url,data,success(data,status,xhr))
  • .forEach()

例:

items.forEach(item=>{
	var name=item.name;
	……
});
或者:
items.forEach(function(item){
	var name=item.name;
	……
});

四、Node应用开发

  • Node:服务器的 JavaScript 运行环境,提供 API 与操作系统互动。
  • npm:Node 的模块管理器,开发 Node 项目的必备工具。
  • Node 开发前端脚本的好:
    • 模块机制
    • 版本管理
    • 对外发布
    • 持续集成的标准开发流程
  • npm 配置文件:package.json –>详情
  • 路由:是指如何定义应用的端点(URIs)以及如何响应客户端的请求
    结构:app.METHOD(path, [callback...], callback)
    • app 是 express 对象的一个实例;
    • METHOD 是一个 HTTP 请求方法;
    • path 是服务器上的路径
    • callback 是当路由匹配时要执行的函数。

node有关命令

  • 安装 Node 模块时,如果指定了 –save 参数,那么此模块将被添加到 package.json 文件中 dependencies 依赖列表中。 然后通过 npm install 命令即可自动安装依赖列表中所列出的所有模块。

一个简单的Node应用

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

1、新建一个目录:
  mkdir simple-app-demo
  cd simple-app-demo

2、创建项目的配置文件:package.json
  npm init -y

3、安装需要的模块:jquerywebpack这两个模块
  npm install -S jquery
  npm install -S webpack
  打开package.json文件,会发现jquerywebpack都加入了dependencies字段,并且带有版本号。

4、 在项目根目录下,新建一个网页文件:index.html

<html>
  <body>
    <h1>Hello World</h1>
    <script src="bundle.js"></script>
  </body>
</html>

5、 在项目根目录下,新建一个脚本文件app.js

const $ = require('jquery');
$('h1').css({ color: 'red'});

6、打开package.json,在scripts字段里面,添加build一行。

  "scripts": {
    "build": "webpack app.js --output-filename bundle.js  --output-path . --mode development",
    "test": "echo \"Error: no test specified\" && exit 1"
  },

7、在项目根目录下,执行将脚本打包的命令:
  npm run build
  执行完成,可以发现项目根目录下,新生成了一个文件bundle.js。
  Error:打包时遇到的错误

8、浏览器打开index.html,可以发现Hello World变成了红色。

9、关于Node的简单应用实现。

REST API

REST是浏览器与服务器通信方式的一种设计风格

REpresentational State Transfer

  • Resource:资源
  • Representation:表现层
  • State:状态
  • Transfer:转换

REST 的核心概念:

  1. 互联网上所有可以访问的内容,都是资源。
  2. 服务器保存资源,客户端请求资源。
  3. 同一个资源,有多种表现形式。
  4. 协议本身不带有状态,通信时客户端必须通过参数,表示请求不同状态的资源。
  5. 状态转换通过HTTP动词表示。

URL:是资源的唯一识别符。


查询字符串 (get请求):表示对所请求资源的约束条件–>GET/zoos/1/animals?limit=10&offset=10


HTTP动词

操作 SQL方法 HTTP动词
CREATE(新建) INSERT POST
READ(查询) SELECT GET
UPDATE(修改) UPDATE PUT/PATCH
DELETE(删除) DELETE DELETE
  • GET /v1/stores/1234
  • PUT /v1/stores/1234
  • POST /v1/stores
  • DELETE /v1/stores/1234

REST API demo

json-server快速“伪造”后台接口

1、 mkdir rest-api-demo
  cd rest-api-demo
  npm init -y

2、 安装模块
  npm install -S json-server

3、在项目根目录下,新建一个 JSON 文件db.json

{
  "posts": [
    { "id": 1, "title": "json-server", "author": "typicode" }
  ],
  "comments": [
    { "id": 1, "body": "some comment", "postId": 1 }
  ],
  "profile": { "name": "typicode" }
}

4、 打开package.json,在scripts字段添加一行。(db.json数据存到服务器)

"scripts": {
  "server": "json-server db.json",
  "test": "..."
},

5、 启动服务器:
  npm run server 三个接口已经生成:

  • posts
  • comments
  • profile
  • 支持post(新增) delete(删除) put(修改) get(查询);

6、打开Postman
依次向http://127.0.0.1:3000/postshttp://127.0.0.1:3000/posts/1发出GET请求 注意:第一个GET请求得到的是key=>posts里的全部items;第二个GET请求得到的是id=1的那个item。

7、向http://127.0.0.1:3000/comments发出POST请求。
注意,数据体Body要选择x-www-form-urlencoded编码,然后依次添加下面两个字段:

body: "hello world"
postId: 1 ![](/images/posts/180311/post1.png) `添加`item到comments,`Body`中为要添加的内容,其中自动生成id。 ![](/images/posts/180311/get3.png)

8、向http://127.0.0.1:3000/comments/2发出PUT请求。
数据体Body选择x-www-form-urlencoded编码,然后添加下面字段:

body: "hello react" ![](/images/posts/180311/put1.png) `修改`comments中`id=2`的item ![](/images/posts/180311/get4.png)

9、向http://127.0.0.1:3000/comments/2发出delete请求 删除comments中id=2的item

10、向http://127.0.0.1:3000/db发送GET请求,得到的就是整个db.json里的数据。

11、ctl+C 终止server。

总结:

  • Node中json-server包:
    • 主要作用是:搭建一台JSON服务器,测试一些业务逻辑

Express 搭建 Web 应用

  • Express:是最常用的Node框架,用来搭建Web应用,有丰富的HTTP工具;
  • Express不对 node.js 已有的特性进行二次抽象,只是在它之上扩展了Web应用所需的功能。
  • Express框架核心特性:
    • 可以设置中间件来响应 HTTP 请求;
    • 定义了路由表用于执行不同的 HTTP 请求动作;
    • 可以通过向模板传递参数来动态渲染 HTML 页面。
  • 整个Express的设计哲学就是利用中间件,不断对HTTP请求加工,然后返回一个HTTP响应。
  • 定义一个路由
  • 中间件:对HTTP请求进行加工

1、npm install安装依赖

2、app1.js
1) 调用express模块,创建一个web实例 –> app

var express    = require('express');
var app        = express();

2) 新建了一个路由对象,该对象指定访问服务器根目录(/)时,服务器响应返回:<h1>Hello World</h1>。将路由加载至/home路径,即访问/home返回Hello World

var router = express.Router();

router.get('/', function(req, res) {
  res.send('<h1>Hello World</h1>');
});

app.use('/home', router);
  • router.get()方法:

    • 第一个参数:服务器上的路径
    • 第二个参数:路由匹配时要执行的回调函数
      • req和res:Express 内置的对象;
      • req:用户的请求;
      • res:web服务器的响应。
  • res.send()方法:服务器响应是送出的内容。

3) 第一行指定外部访问的端口:要么通过环境变量指定,或者环境变量没有指定则默认8080端口。
后两行是启动应用,监听指定端口,并在控制台打印一行文字。

var port = process.env.PORT || 8080;

app.listen(port);
console.log('Magic happens on port ' + port);

4) 通过浏览器访问:localhost:8080/home,输出Hello World

5) 通过环境变量,自定义启动端口。
假定我们指定必须启动在7070端口,命令行可以这样操作:

# Linux & Mac
$ PORT=7070 node app1.js

# windows
$ set PORT=7070
$ node app1.js 浏览器就可以访问`localhost:7070/home`了。

3、app2.js
新增路由,这个路由的路径是一个命名参数:name,可以从req.params.name拿到这个传入的参数。

router.get('/:name', function(req, res) {
  res.send('<h1>Hello ' + req.params.name + '</h1>');
});

node app2.js 启动这个应用。

浏览器访问localhost:8080/home/xuxu,输出Hello xuxu。

4、app3.js
1) body-parser模块的作用:对POST、PUT、DELETE等 HTTP 方法的数据体进行解析;
app.use:用来将这个模块加载到当前应用;
有了这两句,就可以处理POST、PUT、DELETE等请求了。

var express    = require('express');
var app        = express();

var bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({ extended: true }));

2) 新增路由:如果收到了/路径(实际上是/home路径)的POST请求,先从数据体拿到name字段,然后返回一段JSON信息。

router.post('/', function (req, res) {
  var name = req.body.name;
  res.json({message: 'Hello ' + name});
});

node app3.js 启动这个应用。

Postman中,向http://127.0.0.1:8080/home发出一个POST请求.
数据体的编码方法设为x-www-form-urlencoded,里面设置一个name字段.

POST /home HTTP/1.1
Host: 127.0.0.1:8080
Content-Type: application/x-www-form-urlencoded

name=xxuux

服务器返回一段JSON信息。

4、app4.js
1)router.use的作用是加载一个函数,这个函数被称为中间件
  作用是:在请求被路由匹配之前,做一些处理。
  next():代表下一个中间件,表示将处理过的请求传递给下一个中间件。

router.use(function(req, res, next) {
  console.log('There is a requesting.');
  next();
});

2)添加中间件,打印日期:

router.use(function(req, res, next) {
  var date=new Date();
  var year=date.getFullYear();
  var month=date.getMonth()+1;
  var day=date.getDate()
  var str=year+"-"+month+"-"+day;
  console.log(str);
  next();
});

3)get传参:浏览器通过http://127.0.0.1:8080/home?name=xx访问,req.query.name获取参数的值:

router.get('/', function(req, res) {
  res.send('<h1>Hello '+req.query.name+'</h1>');
});

4)占位符传参:浏览器必须严格按照http://127.0.0.1:8080/home/xxx/yyy的形式访问,通过req.params.name获取对应的参数的值:

router.get('/:id/:name', function(req, res, next) {
  res.send('<h1>Hello '+req.params.name+'</h1>');
});

五、前端工程

1、持续集成,Continuous integration(简称 CI):开发代码频繁地合并进主干,始终保持可发布状态的这个过程。

流程:

  • 本地开发(developing)
  • 静态代码检查(linting)
  • 单元测试(testing)
  • 合并进入主干(merging)
  • 自动构建(building)
  • 自动发布(publishing)

优点:

  • 快速发现错误
  • 防止分支大幅偏离主干
  • 让产品可以快速迭代,同时还能保持高质量

2、测试:Web 应用越来越复杂,意味着更可能出错。测试是提高代码质量、降低错误的最好方法之一。

测试的类型:

  • 单元测试(unit testing)
    • Mocha
    • 单元测试是用来对一个模块、一个函数或者一个类来进行正确性检验的测试工作。
  • 功能测试(feature testing)
    • 功能测试必须在真正浏览器做,现在有四种方法:
      • 使用本机安装的浏览器
      • 使用 Selenium Driver
      • 使用 Headless Chrome
      • 使用 Electron
    • 站在外部用户的角度,测试软件的某项功能。与内部代码实现无关,只测试功能是否正常。很多时候,单元测试都可以通过,但是整体功能会失败。
  • 集成测试(integration testing)
    • 持续集成服务平台:代码合并进主干以后,就可以进行自动构建和发布了。
    • 网上有很多 PaaS 平台,提供持续集成服务。
    • Travis CI 就是其中最著名的一个,它可以根据你提供的脚本,自动完成构建和发布。
  • 端对端测试 (End-to-End testing)

以测试为导向的开发模式:

  • TDD:测试驱动的开发(Test-Driven Development)
  • BDD:行为驱动的开发(Behavior-Driven Development)
  • TDD:基于开发者角度,重点测试函数的输入输出
  • BDD:基于使用者角度,重点测试对用户行为的反应

ESLint

  • ESLint:静态代码检查工具
  • 发现语法错误
  • 发现风格错误
  • 自动纠正错误

Mocha

  • Mocha目前最常用的测试框架。
  • mocha是JavaScript的一种单元测试框架,既可以在浏览器环境下运行,也可以在Node.js环境下运行。

Nightmare

  • Nightmare:使用 Electron 模拟真实浏览器环境,功能测试。

Travis CI

  • 持续集成