JS Web
Web 开发原理
-
WEB 应用
- 使用客户端访问服务器的形式。
- 一個完整的 web 应用包含
- 客户端 (浏览器、移动设备)
- 服务端 (服务===>获取静态资源)
- 以 B|S 架构为基础
- C|S 架构(基于网络应用、例如 QQ 、微信、只要装客服端的都是 C|S 架构,服务器会主动往客户端发送数据),弊端:必须下载客户端,就会占内存。
- _clent _ | server
- 客户端 | 服务器
- B|S 架构 (不用下载客户端,输入一个网址,就返回一个页面、数据,处于安全考虑浏览器不会主动往客户端发送数据)。弊端: B|S 受限与浏览器本身,效果以及性能方面比不上 C|S 架构。
- brouser | server
- 浏览器 | 服务器
- C|S 架构(基于网络应用、例如 QQ 、微信、只要装客服端的都是 C|S 架构,服务器会主动往客户端发送数据),弊端:必须下载客户端,就会占内存。
-
web 应用的通信
- 客户端发送一个请求到服务端,服务端接收到这个请求,做一个相应的处理,处理完成拿到一些数据,在将数据返回给客户端,就可以展示给用户看。
- 浏览器向服务器发送请求,服务器接收请求后,响应并返回数据给浏览器。
- Tcp/ip 协议(协议组)
- 应用层( HTTP 协议)
- 传输层( TCP 和 UDP 协议)
- 网络层( ip )
- 物理层( WiFi )
- 通过一层一层的封装、解析(类似于寄快递),计算机之间的通讯必须遵守Tcp|ip协议。
-
http 协议
- Web 通信依赖于 http 协议, 浏览器遵循这个 http 协议,后台就可以获取并解析。
- Hyper Text Transfer Protocol(超文本传输协议)是用于从万维网( World Wide Web )服务器传输超文本到本地浏览器的传送协议。
- HTTP 是一个基于 TCP/IP 通信协议来传递数据。
- HTTP 是一个属于应用层的面向对象(属性和方法是面向对象的表现形式,单独的特性抽象出来)的协议。
-
http 协议的特点
-
超文本传输协议
- 建立链接
- TCP 三次握手(保证数据的安全性)
- 浏览器先发送请求给服务器。
- 服务器收到请求发送回应给浏览器。
- 浏览器确认后,发送正式连接请求。
- TCP 三次握手(保证数据的安全性)
- 浏览器发送请求。
- 服务器处理请求并返回。
- 浏览器接收响应并展示到浏览器上。
- 断开连接。
为了提高效率,使用常连接,不会每次请求都进行三次握手,而是可以设置一个时间,这个时间没有发送过这个请求才会断开连接。即使是常连接,他依旧是无状态。
- 建立链接
-
客户向服务器请求服务时,只需传送请求方法和路径。
-
请求方法常用的有
- GET
- POST
- HEAD
- PUT
-
每种方法规定了客户与服务器联系的类型不同。
-
由于 HTTP协议简单,使得 HTTP 服务器的程序规模小,因而通信速度很快。
-
基于请求/响应的模型。(浏览器发送请求,服务器处理请求并返回数据)
-
无状态(服务器无法存储状态==>例如这次发送登录成功请求,下一次登录时,浏览器并不知道之前登录过,服务器始终认为这是一个新的连接)。
-
无连接(访问一次,连接一次)。
-
请求信息
-
请求行
- 请求名
- 路径 ===> url (请求资源的地址)
- 请求方法( GET、POST )
-
请求头(浏览器告诉服务器的相关的配置信息===>_cookie、JSON_格式、压缩方式等…)
-
空行
-
请求消息体(参数,除了 get 提交以外,其他的参数都是放在请求消息体里, get 是路径里)
GET请求 /index.html?a=1&b=2 注: 在get请求里面参数是携带在/index.html后边,以问号开头a=1&b=2,解析时a=1&b=2为一个字符串,通过正则表达式,将a=1&b=2变成一个对象,a的属性为1,b的属性为2 注: GET 请求问号后面的为参数(相当于传了一个a=1和b=2的数据,传到后台,也称为请求的参数) 举一个登录的例子 要收集用户输入的的账户和密码 ,以GET的方式往后台传就可以写成 GET /index.html?username=zhangsan&password=111111 HTTP/1.1
-
-
响应信息
- 状态行
- 状态码
- 100-199
- 200-299(成功)
- 300-399(资源被移走了之后的处理方法===>302重定向,304===>缓存)
- 400-499(客户端错误,404===>访问不存在的页面,401===>权限不足)
- 500-599(无服务端内部错误,503===>无服务崩了,500===>)
- 状态描述
- 状态码
- 响应头(服务器告诉浏览器的相关的配置信息====>cookie、连接方式等…,这些信息不会展示到页面上)
- 空行
- 响应消息体(展示的页面的数据)
- 状态行
-
NodeJS
-
学习目标
- 怎样在后台搭建一个服务器
- 怎么完成数据持久化,等待访问
- node 使前端从客户端过渡到服务端
- 如何通过 node 搭建 web 应用。
-
概念
- node 就是一个解析器,脱离浏览器,固 node 可以做服务器 即前端可以开发后端。
- node 可搭建后台服务。
- 基于 Chrome V8 引擎的 JavaScript 运行时。(可供 JS 运行的环境,脱离浏览器)
-
特点
-
基于事件驱动。(采用事件驱动的方式,处理异步操作。===>需要花费时间的操作,都丢到任务池)
-
nodejs 是基于非阻塞式 io 的异步运行框架。
- 同步
- 来了一个顾客(一个请求),就分配一个服务员(内存空间),服务员将顾客点的菜交给后厨(后台),后厨开始做菜,厨房得把这个顾客点的菜做好了,交给服务员,服务员端给这个顾客,完成后才会招待下一位顾客。
- 注:一个时间只有一个事情在做,完成后才可进入下一个事情下一个事情。
- 异步
- 来了一个顾客(一个请求),就分配一个服务员(内存空间),服务员将顾客点的菜交给后厨(后台),后厨开始做菜,此时服务员空闲了,可以招待下一位顾客,把下一个才告诉后厨
- 注: 异步就可以做很多事情。
- 进程
- 启动一个程序 计算机里面就是启动了一个进程。(即:分配了一段内存空间,这个空间交给这一个程序进行执行。)
- 线程
- 在进程里面又可以把这个内存分成一块一块的,每一块来运行一个线程。
- 注:没有进程,就不可能有线程。
- 任务池
- 放任务的(即异步的函数) 浏览器端的异步函数 。1、setTimeou 2、 setInterval 3、ajax 的回调
- 阻塞式 IO(输入、输出)
- 类似于同步。
- 非阻塞式 IO(输入、输出)
- 类似于异步。
- 同步
-
JS 就是通过非阻塞式 IO 实现异步操作。(任何需要花费时间的操作,都丢到任务池,同步代码执行完后,才执行任务池的代码)。
-
浏览器内核:
- 渲染引擎
- webkit(起始于苹果公司)
- cocko
- Blink(基于 webkit )
- js 引擎
- V8( Chrome )
- 渲染引擎
-
通过 async/await 异步做远程接口
function promise(time) { return new Promise((resolve, reject) => { setTimeout(function () { resolve(`暂停${time}毫秒`); }, time) }); }; async function times() { let rs = await promise(1000); console.log(1, rs); rs = await promise(2000); console.log(2, rs); rs = await promise(3000); console.log(3, rs); rs = await promise(4000); console.log(4, rs); }; times(); async function _asyncFn(){ const data = await new Promise((resolve, reject) => { request({ type: "post", url: "/users/one", // success表示异步成功,所以就调用resolve方法 success: resolve }); }); console.log(data);
-
Express
- express 快速生成 Web 应用。
- 基于Nodejs的框架。
- 项目结构
- npm
- 是包的管理工具。
- bin
- 运行的文件===> “start”: “node ./bin/www” 使用 npm start 运行的就是 bin
- _routes _
- 路由===>请求以及分发,一个服务提供给浏览器很多的资源,每一个资源通过路由器进行分发,究竟是请求资源
还是接口,由路由来分发
- 路由===>请求以及分发,一个服务提供给浏览器很多的资源,每一个资源通过路由器进行分发,究竟是请求资源
- app.js
- 项目的入口===>所有的应用程序都有一个入口
- _public _
- 静态资源的根目录===>存放静态资源,html 、js 等
- package.json
- 配置文件
- node_modules
- 第三方依赖===>放第三方安装的其他东西
- npm
模块化
-
模块
- 将系统中的各种功能,用某种方式组织起来的一种架构模式。目的是降低功能之间的耦合度,提高重用性和扩展性。(一段代码的集合(一个文件就是一个模块,模块也是代码的集合))
-
模块化
- 同一个应用有很多不同的功能,如果都将各个功能放在一起就会大大增加耦合度,可能修改一个功能,导致另一个功能受到影响,这时就要将一个一个功能封装成一个模块,然后就他导出,需要用到就引入这个模块。每个模块之间是互补干扰的就降低了耦合度,随用随调代码可以进行的重用。
-
模块化开发
- 是为了解决功能之间的高耦合、低内聚和无重用的问题。
-
模块规范分类
- CommonJS 规范(基于服务器的模块规范)
- AMD 规范===> requireJS 异步的模块化定义(预加载、缺点===>预先加载所有模块,有些用不到的模块也会加载,就浪费资源)
- CMD ==> AeaJS 公共的模块化定义(将各种模块化规范的优点集合起来 特点=>既有同步、也有异步)
- UMD 联合(不算是一个规范,只是将其他规范结合在一起)。
- ES6 ===>modure官方的模块化规范。
-
使用模块化的优点
- 提高重用性
- 代码的可用性变强(扩展性,灵活性)
- 避免命名空间的污染()
- 解决模块之间依赖性问题。(不用考虑加载顺序,例如C文件调用B文件,B文件调用A文件,A文件又调用B文件,引用顺序A==>B==>C。如果先引入C,因为C调用的B还没有读取,所以会报错,模块化的开发就可以解决这个问题。)
-
ES6 模块化规范
-
语法
- 导入模块( import )
- 导出模块( export )
-
实例
// 声明时导出 export function f1(){ console.log(11); }; // 声明后导出 function f2(){ console.log(22); }; export{f2} // 声明式默认导出 export default function f3(){ console.log(333); } // 声明后默认导出 function f4(){ console.log(22); }; export{f4 as default} // 引入 import {f1} from "引入文件地址"; // 默认导出的引入不用结构 import 自定义名称 from "引入文件地址"; // JS以模块化引入type="module" <script type="module" src="./src/index.js"></script>
-
MongoDB
- 数据库的分类
- 关系型数据库
- 由表、行、字段组成( SQL 语言)
- mySQL
- Qraole
- 由表、行、字段组成( SQL 语言)
- NOSQL 数据库
- 文档型数据库( JSON MongoDB)
- 键值对数据库
- 对象型数据库
- 关系型数据库
- MongoDB
- 本质上也是一个程序,也是一种服务器应用。
- 操作
- 增 Create
- 删 Read
- 改 Update
- 查 Delete
- 组成
- 集合
- 类似于 JS 里的数组
- 文档
- 一个文档就是一个 JSON 对象
- 文档有一个唯一的标识===>下划线 _
- 字段
- 字段就是 JSON 对象的属性
- 集合
三层架构
- 表现层
- 把数据呈现(例如路由器就在表现层里面)
- 业务层
- 设计业务(提供服务===> service )
- 持久层
- 数据持久化(与数据库打交道)
- DAO 对象
- 完整流程
- 从表现层===>业务层====>持久层===>数据库====>返回持久层===>返回业务层===>在到表现层。
单页应用
- MPA 多页应用
- SPA 单页应用===>(整个应用只有一个页面,利用 JSDOM 来完成节点的替换(进行页面间的跳转))
- 单页开发的优点
- 提高体统的体验度。
- 减少了请求数(页面只有一个)
- 单页开发的缺点
- 增加了开发和维护的难度,但是会涉及到大量节点的操作(可以用框架 vue 和 React 等框架解决)
- 单页不等同于模块化,两者是不同的,但是两者相结合可以达到最好的效果。
多页开发,就会存在很多个html页面,每次加载,就会发送请求,然后刷新除一个新的页面,用户就会明显感受到卡顿,体验很不友好。
-
单页开发实现页面之间的跳转
- 因为只有一个页面,其他的都是 JS 文件,要实现页面间的跳转需借助前端路由,通过匹配不同的路径,利用 JSDOM 来完成节点的替换,从而动态的渲染不同的内容。
- 因为是通过 hash 值的变化,并没有向浏览器发送请求,所以单页开发不仅仅在页面的交互是无刷新的,页面跳转也是无刷新的。
-
前端路由的配置
import Login from "./modules/users/login.js"; import Reg from "./modules/users/reg.js"; import Info from "./modules/info.js"; import Students from "./routes/students.js"; // 节点的替换 var routes = { '/login': () => new Login({ el: "#app" }), '/reg': () => new Reg({ el: "#app" }), //后台页面 '/info': { ...Students, //后台管理系统 on: () => { new Info({ el: "#app" }); }, }, }; var router = Router(routes).configure({ recurse: "forward" }); export default () => { router.init(); // hash锚点,默认路径 // 有路径就把路径传给location.hash,没有就用默认值 location.hash = location.hash || "#login"; }
前后端分离
-
允许跨域访问的资源
- js
- css
- 图片
- link img scrtion 标签可以访问其他服务器上的资源(天生就能跨域,请求类型是 GET )
-
不允许跨域访问的资源
- ajax 请求(因为 ajax 操作可以对服务增删改查等…操作,所以是为了安全考虑。)
-
什么是跨域?
- 访问的内容,来自于两个不同的服务器( ip 、协议、端口任意一个不同)就会形参跨域。
-
什么是同源?
- 浏览器里面有一种安全策略(同源策略)
-
为什么会形成跨域?
- 跨域是同源导致的, 跨域是因为浏览器里面有一种安全策略(同源策略),由于同源策略导致ajax只能访问我的当前服务,要访问其他服务就形成了跨域(即=>我在一个服务器上边,想要请求其他服务器上的资源)
-
怎样解决 ajax 的跨域访问?
-
CORS
-
是一个在服务器端设置一个响应头。(这个请求头允许其跨域)
-
在 app.js 中添加
app.use('/*',function(req,res,next){ res.setHeader("Access-Control-Allow-Origin","*"); res.setHeader("Access-Control-Allow-Headers", "X-Requested-With,Origin,Content-Type,Accept"); // 服务器支持的头信息 res.setHeader("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS"); // 允许的方法 next(); })
-
缺点===>需要修改服务器,带来隐患。
-
-
JSONP (实际上是伪造 script 请求)
-
jsop 不是 ajax 技术,浏览器利用 script 标签,模拟发起一个请求跨域访问服务器,服务器把返回的数据,在外面包裹一个方法名称,浏览器接收到一个服务器返回的数据,执行他对应的函数,从而得到数据。
-
ajax 使用 JSONP
ajax使用JSONP: text({username,password}){ $.ajax({ url:"http://localhost:3001/users/login", data:{username,password}, dataType:"jsonp", success(data){ consols.log(data) } }) } 注:请求时添加了一个 dataType:"jsonp"
-
JSONP 的缺点
-
使用jsop就意味着后台只能暴露 GET 请求。
-
后台返回的数据还得专门处理。
处理返回的数据: const {username,password,cbName}=req.query; const data =await usersApi.login({username,password}) res.jsonp(data);
-
-
-
代理服务器(通过中间服务器转发)
-
通过中间服务器来访问后端服务器的内容,从而实现浏览器的跨域访问。
-
完成步骤
-
下载代理服务器插件
npm install --save http-proxy-middleware
-
在中间服务器上配置代理,app.js中加入如下代码
const {createProxyMiddleware } = require('http-proxy-middleware'); const restream = function(proxyReq, req, res, options) { if (req.body) { let bodyData = JSON.stringify(req.body); // incase if content-type is application/x-www-form-urlencoded -> we need to change to application/json proxyReq.setHeader('Content-Type','application/json'); proxyReq.setHeader('Content-Length', Buffer.byteLength(bodyData)); // stream the content proxyReq.write(bodyData); } } const options = { target: 'http://localhost:3001', // 目标服务器的 host changeOrigin: true, // 默认 false,是否需要改变原始主机头为目标 URL pathRewrite: { // 重写请求 '^/api': '/', // 所有以 "/api" 开头的请求,"/api" 都会重写为 "/" }, onProxyReq:restream } // 该行代码必须写在 var app = express(); 之后 app.use('/api', createProxyMiddleware(options));
-
-
-