第一章
用户在浏览器中输入 www.taobao.com 直到看到页面之间发生了什么?(浏览器的加载、解析、渲染过程)
① 操作系统(windows)访问网络上DNS服务器,把域名转换为IP地址
DNS [www.taobao.com 60.28.242.250]
② 浏览器向Web服务器发起HTTP请求
③ web服务器接收并解析请求消息,查找指定资源,可能访问数据库,构建并返回http响应消息
④ 浏览器接收并解析响应消息,发送请求获取嵌入在HTML中的资源(如图片、CSS、JS、视频、音频等)
⑤ 浏览器(缓存)接收到解析内容,并解析和渲染响应内容
解析html构建DOM树→构建渲染树(render tree)→布局(layout)→绘制(painting)rendering树
布局(layout): 根据渲染树将节点树的每一个节点布局在屏幕上的正确位置
创建渲染树后,下一步就是布局(Layout),也叫回流(reflow,relayout):
这个过程就是通过渲染树中渲染对象的信息,计算出每一个渲染对象的位置和尺寸,将其安置在浏览器窗口的正确位置,而有时会在文档布局完成后对DOM进行修改,这时候可能需要重新进行布局,也可称其为回流,本质上还是一个布局的过程,每一个渲染对象都有一个布局或者回流方法,实现其布局或回流
在渲染树布局完成后,再次操作文档,改变文档的内容或结构,或者元素定位时,会触发回流,即需要重新布局
$("div").css("margin-left","10px");
$("div").css("margin-left","100px");
此时并没有效果,因为对margin-left的修改并没有触发回流,元素margin-left值的改变被缓存,可以在中间强制触发回流
$("div").css("margin-left","10px");
$("div").css("margin");
$("div").css("margin-left","100px");
绘制(painting): 也叫重绘,遍历渲染树绘制所有节点,为每一个节点适用对应的样式
通常,当改变元素的视觉样式,如background-color, visibility, margin, padding或字体颜色时会触发全局或局部重绘
静态网页和动态网页
静态网页: 网页内容任何人在任何时间访问都是不变的: HTML/CSS/JS/Flash/音频/视频...
动态网页: 网页内容任何人在任何时间访问可能都是不同的: JSP/PHP/NodeJS...
① JSP = HTML+JAVA 功能强大可靠,适合大型企业级项目
② PHP = HTML+PHP 简单易用,适合互联网项目
③ ASP.NET = HTML+C# 易用,适合windows平台,但价格昂贵
④ NodeJS = HTML+"JS" 性能好,适合服务器IO密集(如微博)型项目,不适合CPU密集(如天气预报)型项目
_
Nodejs概述
Nodejs 不是JS,而是一个软件开发平台(安装在服务器端),只不过使用的语言是JS,它的竞争对手是PHP/JSP/ASP.NET,历史上第一次有一种语言可以通吃前后端
前端JS和Node.js区别:
前端JS: 运行于客户端浏览器中,存在兼容性问题;数据类型: 值类型+引用类型(ES+DOM+BOM+自定义)
Node.js: 运行于服务器端(V8引擎),不存在兼容性问题;数据类型: 值类型+引用类型(ES+扩展对象+自定义)
官网: www.nodejs.org
版本: 0.12(16年初) → 4.x(16年中) → 6.x(16年底)
LTS: Long Term Support 长期稳定支持版本
Current: 最新版本(不太稳定)
安装nodejs
① 区别32 windows/64 windows: 计算机 → 右键 → 系统类型
32位安装: node-v6.10.2.x86.msi
64位安装: node-v6.10.2.x64.msi
② windows系统下,双击安装文件
建议1: 安装目录不要c:\
建议2: 安装目录不要中文,不要有空格
③检测是否安装成功: 开始 → 运行 → cmd → node -v,显示: v6.10.2 即安装成功
Node.js的两种运行模式
① 交互模式(REPL: Read Evaluate Print Loop)一般用于临时测试,输入一行代码执行一行
注意: 交互模式自带输出功能,不必写console.log(...),但是可以在程序中 写 用来查看
node 回车,进入交互模式
.exit 退出交互模式
② 脚本模式,正式项目中使用的方式
把要执行的所有语句编写的一个文本文件中(后缀名为.js或不写),一次性提交给node解释器执行
node 完整路径名/x.js 回车 //在cmd.exe下
按住Shift+右键 → 在此处打开命令窗口 → 输入相对路径名 回车
提示: 只要安装完Node.js,重启一下WebStorm,WebStorm可以自动发现node.exe解释器程序
Run → Run 文件名 → 运行程序
新建的项目要修改默认的文件编码方式为 UTF-8
_
如何自学一门新语言 ——— Node.js
① 了解背景 ——— 百度百科Node.js
② 搭建开发环境,编写HelloWorld,下载并安装node-v6.10.2-x64.msi,使用(包含)V8引擎
交互模式、脚本模式
③ 数据类型 ——— 重点面试题
前端JS中的数据类型:
① 基本/原生/原始/值类型: string、number、boolean、null、undefined
② 引用/对象类型
ES对象类型: String/Number/Boolean/Date/RegExp/Object/Function/Error/Array/Window/Math
BOM对象类型: window、document、screen、history、location、navigator、event...
DOM对象类型: Node、Element、Attr...
用户自定义对象类型: { }
后端Node.js中的数据类型:
① 基本/原生/原始/值类型: string、number、boolean、null、undefined
② 引用/对象类型
ES对象类型: String/Number/Boolean/Date/RegExp/Object/Function/Error/Array/Window/Math
Node.js官方与第三方对象: 目前有几百个... www.npmjs.com
没有DOM和BOM
用户自定义对象类型: { }
④ 变量和常量: var age = 20; const PI = 3.14;
⑤ 运算符: 算术运算符
比较运算符
逻辑运算符
位运算符
三目运算符
赋值运算符
特殊运算符: instanceof typeof
⑥ 逻辑结构: 循环结构: while do...while for( ; ; ) for(...in ...) for(...of ...)
选择结构: if...else... switch...case...
⑦ 通用小程序: 九九乘法表、100以内的质数、数组排序...
⑧ 函数和对象
⑨ 常用的组件、第三方工具、框架
⑩ 实际小项目
_
Node.js中的特有概念 ——— 模块(Module)
Modal: 模态框,登录时的遮罩层
Model: CSS框模型,定义元素框处理元素内容,内边距以及外边距的方式
Module: 一个Web项目功能可以分为很多不同的"模块",如商品管理模块、用户管理模块、支付模块、促销模块、商家管理模块...
Node.js按照功能的不同,可以把函数、对象分别保存到不同的文件、目录下,这些文件/目录在Node.js中就称为"Module"模块
Node.js中每个模块都是一个独立的构造函数,解释器会为每个模块(.js)文件添加如下代码:
(function(exports, require, module, __filename, __dirname){
size(); //自己编写的文件内容
} )
//exports: 空对象{ } 用于声明向外部导出自己的成员
//require: 函数fn 用于导入其它的模块,创建指定模块对象 new
//module: 代表当前模块
//__filename: 当前文件的绝对路径名
//__dirname: 当前文件所属目录的绝对路径名(后面加'..',表示上一级)
面试题: Node.js的模块中exports和module.exports对象的区别是什么?
相同点: 二者都可以用于向外界导出自己内部的成员
不同点: module 变量指代当前模块对象,真正导出的是module.exports
Node.js 底层有代码: exports = module.exports
module.exports可以向外导出对象或成员,exports只能向外导出成员
所以: 若只是给exports对象添加新的成员,则等价于给module.exports添加新成员
但是若修改了exports的指向,则不会产生实质作用
每个模块都可以使用自己的require()函数引入另一个模块,底层本质就是创建了指定模块的一个对象实例
引用模块:
var obj=require('./要引入的模块文件名'); // ./:当前目录下,后缀名可不写,返回值是个对象
使用: obj.成员名; 或 obj.成员名(); //变量不加(),函数加(),原函数若无形参则无法传参
每个模块可以使用exports对象向外导出/公开一些自己内部的成员供其它的模块使用
要公开的成员:
exports.成员名 = 成员值; //成员名: 调用时使用的名称;成员值: 内部成员名;函数也不加()
Node.js中模块的分类
① Node.js官方提供的原生模块 ——— 安装在解释器内部: var 变量名 = require('模块名');
global、querystring、url、fs、http、Buffer...
② 第三方编写的模块: mysql、oracle、express...
③ 用户自定义的模块: 文件模块和目录模块
Node.js预定义模块 ——— Global
该模块提供的可以直接使用,而无需 require('global')
exports: 用于向外部导出当前模块内部的成员
module: 用于指代当前模块
require: 用于引入其他模块
__filename: 返回当前模块的文件全名(绝对路径)
__dirname: 返回当前模块所处目录的全名(绝对路径)
console: 指代控制台对象,注意该对象与Chrome中console不同
console.log/dir/time/timeEnd/assert ... //console.log与Chrome中的console.log类似
timer模块(全局API,无需调用require('timers')),用于在某个未来时间段调用调度函数
Node.js中的计时器函数实现了与Web浏览器提供的计时器类似的API,但使用了不同的内部实现,是基于Node.js事件循环构建的
启动定时器:
默认情况下,当使用setTimeout()或setInterval()预定一个定时器时,只要定时器处于活动状态,Node.js事件循环就会继续运行。这些函数返回的Timeout对象都导出了可用于控制这个默认行为的timeout.ref()和timeout.unref()函数
定时器都会返回一个用于清除定时器的变量
立即定时器(I/O事件的回调之后立即执行)
setImmediate(callback[, ...args]) //callback: 在事件循环的当前回合结束时调用的函数
//arg: 当调用callback时要传入的可选参数
周期性定时器(等待delay毫秒之后重复执行callback)
setInterval(callback,delay[, ...args]) //当delay大于2147483647或小于1时,delay被设为1
一次性定时器(预定在delay毫秒后执行的单次callback)
setTimeout(callback,delay[, ...args])
取消定时器:
clearImmediate(immediate)
clearInterval(timeout)
clearTimeout(timeout)
第二章*
自定义模块的两种形式
① 文件模块: 创建一个js文件,如m3.js,导出需要公开的数据。其它模块可以require('./m3')模块
② 目录模块
方式1: 创建一个目录,假设名为m4,其中创建名必须为index.js文件,导出需要公开的数据。其它模块可以require('./m4')模块 m4/index.js require("./m4");
方式2: 创建一个目录,假设名为m5,其中创建名package.json文件,其中声明main属性指定默认执行的启动js文件,如'./5.js',其中导出需要公开的数据。其它模块可以require('./m5')模块
m5/package.json "main":"./1.js"
1.js
方式3: 创建一个目录,必须名为node_modules,在其中再创建目录模块,假设名为m6,在其中创建文件,必须名为package.json,其中声明main属性指定默认执行的启动js文件名,如'./6.js',其中导出需要公开的数据
其它模块在node_modules的同级目录可以require('m6')模块 //只有这1种不加 ./ (若加会报错)
node_modules/m6/package.json "main":"./6.js"
6.js
_
NPM包管理器
Node Package Manager: Nodejs的第三方模块/包管理器,用于下载、删除、更新、维护包依赖关系的工具
npm工具默认到www.npmjs.org网站下载所需的第三方模块/包
标准语法:(在cmd.exe下),在哪里打开命令窗口,就自动下载到哪里
下载一个新的软件包: npm install 包名,比如: npm i mysql
删除指定的软件包: npm uninstall 包名
更多的 npm 命令参数可以使用: npm -h 进行查看
_
① Node.js官方提供的原生模块 ——— querystring: 查找字符串
querystring模块用于处理HTTP请求 URL中查询字符串
const qs = require("querystring"); 加载querystring模块
var obj = qs.parse(str); 把任意查询字符串转换为JS对象
var str = qs.stringify(obj); 把JS对象转换为查询字符串
JSON.stringify(result); 将js数据转换为JSON字符串
② Node.js官方提供的原生模块 ——— url
url模块用于解析一个HTTP请求地址,获取其中各个部分
const url = require("url"); 加载url模块
var obj = url.parse(str); 把一个URL字符串解析为一个对象
var obj = url.parse(str,true); 把URL字符串解析为一个对象,并把其中的查询字符串也解析为对象
③ Node.js官方提供的原生模块 ——— Buffer 重点
Buffer: 缓冲区,用于暂存以后要用到的数据(可能是数字、字符串、二进制图片/音视频等)
缓存是CPU的一部分,它存在于CPU中,CPU存取的速度可达1G/秒,而内存最多几十M,缓存是用来解决CPU与内存的速度差异问题的。内存中被CPU访问最频繁的数据和指令被复制入CPU中的缓存,这样CPU只需要到缓存中去取数据就可以了,而缓存的速度要比内存快的多
Buffer模块是全局模块,可以直接使用,无需加载
var buf1 = Buffer.alloc( 1024*n ); //创建一个指定大小的缓冲区(最大2M),1024:字节byte,简写为b
var buf2 = Buffer.from( [1,3,5] ); //创建一个缓冲区,用来存放数字数组
var buf3 = Buffer.from( 'abcdefg' ); //创建一个缓冲区,用来存放字符串
var str = buf3.toString(); //把一个缓冲区中的数据(buf)转换为字符串
④ Node.js官方提供的原生模块 ——— fs 重点
fs模块提供了对文件系统中的文件/目录进行增删改查、读写的功能
同步: NodeJS解析器 与 磁盘: 一个工作时,另一个等待
异步: NodeJS解释器 与 磁盘: 可以同时工作
const fs=require("fs"); //加载fs模块
var data = fs.readFileSync("./文件路径"); //同步(Synchronize)读取文件中的内容
fs.writeFileSync("./文件路径",str/buf); //同步向文件中写入内容(同时删除已有内容)
//写入的文件如 ./public/2.log不必先创建,nodejs发现如果没有此文件,会先创建再写入
fs.appendFileSync("./文件路径",str/buf); //同步向文件中追加写入str/buf内容(不删除已有内容)
fs.readFile("./文件路径",function(err,data){...} ); //异步读取文件中的内容,data: 返回的内容
fs.writeFile("./文件路径",str/buf,function(err){...} );//异步向文件中写入内容(删除已有内容)
fs.appendFile("./文件路径",str/buf,function(err){...} ); //异步向文件中追加写入str/buf内容
(不删除已有内容)
if(err) throw err; //抛出错误,用于出错时的提示
注意: 在项目中,文件读写可以使用同步或异步,但推荐使用异步方法: 可以最大限度的发挥Nodejs的优势
_
⑤ Node.js官方提供的原生模块 ——— http 重点
HTTP模块可用于编写基于HTTP协议的客户端程序(即浏览器),也可以编写基于HTTP协议的服务器端程序(即Web服务器)
用http模块编写一个Web服务器:
var server = http.createServer(); //创建http服务器
server.listen(8080); //让http服务器监听端口,当客户端发起此端口请求时,连接服务器
//范围: 1-65535,1023以下是互联网公共程序使用,最好不用)
server.on('request',function(req,res){ //绑定请求事件request
res.setHeader("Content-Type","text/html;charset=utf-8") 设置响应主体格式(绑定事件后)
req 请求对象request(客户端请求信息),解析请求消息
req.method 客户端请求方式
req.httpVersion 客户端请求版本
req.url: 客户端请求程序地址
var path=url.parse(req.url).pathname 客户端请求路径pathname(对象格式)
var objUrl=url.parse(req.url,true):
//.pathname: 如: '/login.html',带后缀,?之前的字符
//.path: 如: '/login.html?uname=tim&uid=10'
//.query: 发送的字符串,如: { uname: 'tim', uid: '10'
url.parse(str,true);时才是对象,否则为字符串
//.search: 如: '?uname=tim&uid=10'
res 响应对象response(服务器输出信息),向客户端输出响应消息
res.statusCode=200 设置响应状态码为200 (默认200,可以省略)
res.write("html标签") 输出响应主体(也可输出data数据)
res.end(data); 输出数据后结束(也可换成html标签)
} ) 若想用ajax,需要将data放在函数中,函数名要与ajax的jsonpCallback的值一致
一个端口只能启动一个服务器,否则会报错
'rn': 换行回车,用于追加输出数据
加载模块放在最上面,否则可能会出错
用户请求先经过服务器,再连接数据库
查看端口状态是否驱动: netstat -an/ano //在cmd.exe下
第三章*
使用Node.js访问MySQL服务器
为了精简Node.js解释器,官方没有提供任何访问数据库的相关模块,必须使用npm工具下载第三方模块
在www.npmjs.org上搜索关键字mysql,可以得到很多相关的模块,每个模块都有使用说明
使用npm工具下载mysql模块: npm i mysql //在cmd.exe下
mysql 模块的使用步骤:
① 创建与服务器数据库的连接:
const mysql = require('mysql');
var conn = mysql.createConnection({
host:"127.0.0.1", //服务器主机IP地址
user:"root", //mysql用户名
password:"", //mysql密码
database:"jd" //mysql数据库名
});
② 创建SQL语句,发送给服务器数据库执行:
$sql="insert into jd_user values(null,?,?)"; //用 ? 占位符预先输入数据
//代替字符串/日期要加的引号,常规入侵手段失效
conn.query($sql,["tom","123"],function(err,result){});
③ 关闭连接: conn.end();
_
为了提升数据库操作性能,nodejs提供了完善功能的连接池(应用完后归还连接)
工作机制:
在系统启动时自动创建多个连接,保存在内存中,当应用需要连接时,租用一个连接,应用完成后归还连接
Nodejs使用连接池操作 mysql 的使用步骤:
① 创建数据库连接池:
const mysql = require('mysql');
var pool = mysql.createPool({
host:"127.0.0.1", //服务器主机IP地址
user:"root", //mysql用户名
password:"", //mysql密码
database:"jd", //mysql数据库名
connectionLimit:10 //指定系统启动时最小连接池数量,默认为10
});
② 从连接池获取其中一个连接:
pool.getConnection((err,conn)=>{
if(err){ throw err };
var sql="insert into jd_user values(null,?,?)"; // var sql=... <=> $sql=...
conn.query(sql,['tom',obj.p],(err,result)=>{//result是对象组成的数组(查询)/对象(添加)
(日期至少要写年月日) //result.length==0判断是否成功
if(err){ throw err };
else if(result.affectedRows>0){ //添加记录影响的行数>0,表示操作成功,result表示返回结果
console.log("添加成功:"+result.insertId); //添加记录后,自增的Id值
conn.release(); //③ 释放连接,回到连接池。不必conn.end(),但要先res.end(),输出结束
若不释放,服务器数据库运行速度会越来越慢
}
})
});
第四章*
NodeJS 框架 Express
使用官方提供的http模块可以创建一个web服务器应用,但是此模块非常底层,要处理各种情形,比较繁琐。推荐使用http模块进一步封装简化的模块: express (第三方模块)
第三方模块,是基于nodejs的http模块而编写的高层模块,简化了web服务端应用的开发,express是一个请求处理工具,用于接收客户端请求消息,返回响应消息
可以到 npmjs.org 上下载该模块: npm i express
该模块自己的官网: http://expressjs.com
该模块的中文镜像网站: http://www.expressjs.com.cn
使用方法:(框架里包含简单的404页面)
① 在自己的模块中引入express模块:
const http = require("http"); //加载http模块
const express = require("express"); //加载express模块
② 使用http模块创建一个web服务器对象,让 express() 处理请求
var app = express(); //创建express对象
var server = http.createServer(app); //创建web服务器
server.listen(8080); //绑定监听端口
请求方法:① 在浏览器地址栏输入127.0.0.1:8080/请求路径
② a/img/script/link的href/src属性="/请求路径"
③ ajax的url设置为"/请求路径"
④ ajax的url设置为"http://127.0.0.1:8080/请求路径"(不同域需要跨域,必须加http)
app.get('/请求路径',处理函数(req,res)=>{ //路由: 处理get请求,根路径: "/" 不加.点
//express中的req和res继承http模块中的req、res,但功能上有很大增强
res.send(...); //res.send()是express提供的简化版,自动识别发送的格式
//res.send() 相当于 res.setHeader()+res.write()+res.end()
res.sendFile(__dirname+"/public/index.html"); //__dirname: 当前文件所属目录的绝对路径名
//res.sendFile() 相当于 fs.readFile()+res.setHeader()+res.write()+res.end()
//路径: 相对路径(以当前程序路径为准)/绝对路径(以磁盘路径为准)
res.json(result); //将 js数据(可以是数组/对象等) 转化为JSON字符串,输出给客户端
//res.json() 相当于 JSON.stringify(result)+res.setHeader()+res.write()+res.end()
});
发送JSON格式数据的好处: 程序灵活,适用于各种不同的环境(ajax/ios/安卓/...)
不同的请求方式,请求路径可以相同,但应避免相同
express路由: 路由(Route) = Method(请求方式)+Path(url地址)+fn(处理函数)
_
如何使用express 4.0 处理静态资源(jpg/html/css/js...)文件
官方提供了函数(中间件,Middleware): app.use(express.static('public目录'));(不加 / )
若客户请求了/public目录某个指定资源("style.css"),它可以直接向客户端返回,不会再调用后续路由
若请求的是public下的子文件夹中的文件,则请求url改为: "login.html"
app.get("/",(req,res)=>{res.redirect("login.html");}) //自动跳转到指定页面
开发时,公共资源与以前相同,开发好后,把文件整体放在public下即可(使用ajax时要用服务器)
express是一个自身功能极简,完全是由路由和中间件构成的web开发框架: 从本质上说,一个express应用就是调用各种中间件函数
中间件是一个函数,它可以访问请求对象(request),响应对象(response)
中间件工作于请求和响应之间,与路由混合使用
中间件的功能包括:
① 执行任何代码
② 修改请求和响应对象
③ 终结请求—响应循环
④ 调用堆栈中的下一个中间件
标准语法
app.use(url,(req,res,next)=>{
//中间件要执行的代码
next(); //调用下一个中间件或者路由
});
_
① 引入各种模块
② 用http模块创建服务器对象(var server = http.createServer(app))
③ 创建数据库连接池(var pool = mysql.createPool)
④ 判断请求(request): app.get/app.post...
⑤ 从连接池获取连接(pool.getConnection),并执行相应的操作(操作mysql数据库(conn.query))
⑥ 发送数据res.send(),并释放连接conn.release()
客户端JS与服务器的NodeJS整合在一个项目中
① 客户端请求静态HTML页面
② 服务器返回客户端请求静态资源 app.use(express.static("url"))
③ 客户端加载完成,AJAX异步请求动态数据
④ 服务器返回动态数据(一般数据都是JSON格式)
⑤ 客户端异步动态数据,解析出来,挂载到DOM树上
_
GET: 表示客户端想获得指定的资源
发起方式: 浏览器地址栏输入URL、超链接、标签href /src、js跳转、表单GET、AJAX-GET
POST: 表示客户端想添加/上传指定数据给服务器,相关数据在请求主体中
发起方式: 表单-POST提交、AJAX-POST
PUT: 表示客户端想"放置/更新"服务器上指定资源,相关数据在请求主体中
DELETE: 表示客户端想删除服务器上指定资源
PUT和POST语义区别:
所有请求方法中只有二个有请求主体,都可以向服务器传送数据
PUT: 一般情况表示更新,多次请求也只能产生一条影响
POST: 一般情况表示添加,每次POST请求都在服务器上产生一条数据记录
GET /user 表示客户端想获取所有用户数据
GET /user?pno=2&psize=10 表示客户端想分页获取数据
请求字符串: /user?uid=10&loc=bj
app.get("/user",(req,res)=>{
req.query.uid; //express为每一个req对象添加 query(查询) 属性
req.query.loc;
});
GET /user/pno/2/psize/10 表示客户端想分页获取数据
请求字符串: /user/jsj/60 //请求时不用加:
app.get("/user/:type/:id",(req,res)=>{
req.params.type; //express为每个req对象添加 params(参数) 属性
req.params.id;
});
GET /user/10 表示客户端想获取编号为10数据
POST /user uname=tom&age=10 表示客户端想添加一条记录
app.post(url,(req,res)=>{
req.on("data",(data)=>{ //为req对象绑定事件,接收请求主体的数据
var obj = qs.parse(data.toString());//把收到的数据(buf)转为字符串
//再把任意字符串转换为JS对象
//POST请求会把数据统一转换为Buffer数据发送给服务器
})
});
PUT /user uname=james&uid=1 表示客户端想更新一条记录
app.put(url,(req,res)=>{
req.on("data",(data)=>{ //为req对象绑定事件,接收请求主体的数据
var obj = qs.parse(data.toString());
})
});
DELETE /user 表示客户端想删除服务器所有用户数据
app.delete(url,(req,res)=>{});
DELETE /user?uid=9 表示客户端想删除服务器编号为9用户
DELETE /user/9 表示客户端想删除服务器编号为9用户
_
发送json过程中,php与nodejs 的区别
php与nodejs都是服务器端技术,发送json字符串
php
一个对象: echo '{"uname":"tom","code":1}';
一个数组: echo json_encode($rows);
nodejs(express)
一个对象: res.json({code:1,bid:10});
一个数组: res.json(result);
AJAX 接收一个对象
$.ajax({
url:"/book",
success:function(data){
console.log(data.bid);
}
});
AJAX 接收一个数组
$.ajax({
url:"/book",
success:function(data){
for(var i=0;i var o = data[i];
} }
});