基础
-
Node.js 没有 BOM、DOM,使 JS 可不通过浏览器运行,是 JS 的运行时环境,构建于Chrome 的 v8 引擎(专门处理JS的虚拟机,一般附带在网页浏览器中,解析执行JS代码),
-
REPL: Read–eval–print loop
-
服务器重定向针对异步请求无效
-
header(‘location’)
- 301 永久重定向,浏览器会记住
- 302 临时重定向,浏览器不记忆
-
同一个对象的两个引用若其中一个重新赋一个对象,则被赋对象的那个和另一个指的不是同一个,赋对象中的某个值没事,若重新 = 回来,则还是指同一个对象
1
2
3
4
5
6
7
8
9
10
|
// 模块中默认有一句代码:
// var exports = module.exports
// 这里导致 exports !== module.exports
module.exports = {
foo: 'bar'
}
// 这里重新建立两者的引用关系
exports = module.exports
|
1
2
3
4
5
6
|
exports.a = 123
exports = {}
exports.foo = 'bar'
module.exports.b = 456
// result:
// a:123, b:456
|
-
markdowm css 避免自动升级覆盖:github.css 复制,重命名 github.user.css
-
npm install 加 –save 让 package.json(可通过 npm init 自动初始化出来) 中的 dependencies 选项保存第三方包的依赖信息。
- npm install :自动把 package.json 中的 dependencies 中所有依赖项下载回来
- npm uninstall 包名 :只删除,若有依赖仍然会保存
- npm uninstall –save 包名 :删除的同时把依赖信息也去除
-
package-lock.json 锁定版本号,防止自动升级新版
-
对密码进行 md5 重复加密 body.password = md5(md5(body.password))
-
模块查找机制
- 缓存(所以在循环加载时不会重新加载)
- 核心模块
- 路径形式的模块
- 第三方模块(当前目录找不到则往上一个目录找)
-
nodemon 修改完代码自动重启,npm –global 安装
-
第三方模块下的 package.json 中的main属性记录了模块的入口模块,默认是 index.js
-
代码风格(JavaScript Standard Style / Airbnb JavaScript Style(更严谨))若无“;”,则当一行代码以"("、"["、"" 开头, 前面要加 ";"。 e.g. ;
hello`.toString()
` 是 EcmaScript 6 新增的字符串包裹方式,叫模板字符串。
1
2
3
4
|
var foo = `
adasd add
adads
`
|
1
2
3
4
|
app
.get('/login', 函数1)
.get('/asdfaf', 函数2)
...
|
extend
1
|
extend(obj1, obj2) // obj2 有了 obj1 的所有成员了
|
方法
1
2
3
|
var arr = [10, 5, 20] // [5]
arr.splice(1, 1)
// arr = [10, 20]
|
文件/模块 路径
1
2
3
4
5
6
|
require('./b') // OK
require('./b.js') // OK
require('b') // error
require('/afa/asf.js') // 磁盘根目录
require(加载文件模块并执行里面的代码,拿到被加载文件导出的接口对象) 返回别的文件 exports 的对象,若被 require 的文件无 exports,则默认返回空(0)。将所有需要被外部访问的成员对象 exports
exports.add = function(x, y){ return x + y }
|
创建服务器
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
26
|
var server = http.createServer();
server.on('request', function(req, res){
var url = req.url;
if(url === '/'){
fs.readFile('./a/index.html', function(err, data){
if(err){
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
res.end('file read error');
}else{
res.setHeader('Content-Type', 'text/html; charset=utf-8');
res.end(data);
}
})
}else if(url === '/hhy'){
fs.readFile('./a/asd.jpg', function(err, data){
if(err){
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
res.end('file read error');
}else{
// 也可以通过 <meta charset="UTF-8"> 实现
res.setHeader('Content-Type', 'image/jpeg; charset=utf-8');
res.end(data);
}
})
}
})
|
模板引擎
将得到的文件名和目录名替换到 template.html 中:模板引擎,或在template.html中预留一个特殊的标记;根据 files 生成需要的 html 内容 ;替换
1
|
<tbody id="tbody">HHY</tbody>
|
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
|
//server.on ...
fs.readFile('./template.html', function(err, data){
if(err){
return res.end('404 Not Found.')
}
// 得到目录列表中的文件名和目录名: fs.readdir // 类似Apache FTP
fs.readdir(wwwDir, function(err, files)){
if(err){
return res.end('Can not find Dir')
}
var content = ''
files.forEach(function (item){
content += `
<tr>
<td data-value="apple"><a>${item}<a></td>
<td></td>
<td></td>
</tr>
`
})
})
data = data.toString()
data = data.replace('HHY', content)
res.end(data)
})
|
模板引擎只关心自己能认识的模板标记语法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
<script type="text/template" id="tpl">
大家好,我叫{{ name }}
我喜欢 {{each hobbies}} {{ $value }} {{/each}}
</script>
<script>
var ret = template('tpl',{
name: 'HHY',
hobbies:[
'唱',
'跳',
'Rap',
'篮球'
]
})
</script>
|
- 客户端渲染(ajax,异步),至少两次请求。第一次,页面;第二次,动态数据。可尽早看到页面(用户体验好)。爬虫爬不到。
- 服务端渲染,一次请求。增加服务端压力。SEO。
表单提交方法 ???
action
是表单提交地址(请求的url
地址)
method
请求方法
post 数据放到请求体里
get 数据放到 url 中
- 默认
1
2
3
|
<form action="asdf.php" method="get">
</form>
|
- 异步
exports
- 如果一个模块需要直接导出某个(单个)成员,而非挂载的方式,若多个,后面会覆盖前面的
1
2
|
// 导出单个成员
module.exports = add
|
1
2
3
4
5
6
7
8
|
// 导出多个成员
// 方法1
exports.add = add // function
exports.a = "asdas"
// 方法2
module.exports = {
}
|
- 默认在代码的最后一句有:
return module.exports
express 框架
web 开发框架之一,提高开发效率
1
2
3
|
res.redirect('/')
// res.statusCode = 302
// res.setHeader('Location', '/')
|
模板引擎 art-template
1
2
|
npm install --save art-template
npm install --save express-art-template
|
- config
- 第一个参数表示当渲染以
.art
结尾的文件的时候,使用 art-template 模板引擎。
1
|
app.engine('art', require('express-art-template'))
|
- usage
- express 为 response 相应对象提供了方法:render。默认不可用,若配置模板引擎就可以用了。
res.render('html 模板名', {模板数据})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
app.get('/', function(req, res){
res.send('hello world') // 相当于 write
// express 默认去 views 目录中找 index.html
// 开发人员默认所有把视图文件都放到 views 目录中
res.render('index.html', {
title: 'hello world'
})
res.render('index.html',{
fruit:['苹果','香蕉', '橘子']
})
res.render('index.html',{
// 文件读到的是字符串,需要手动转成对象
students: JSON.parse(data).students
})
}
|
1
2
3
4
5
6
|
{
"students":[
{"id": 1, "name": "asd"},
{"id": 2, "name": "adaasd"}
]
}
|
1
2
|
// 第一个参数 views 不要写错
app.set('views', render函数的默认目录路径)
|
语法积累
相同的页眉页脚
1
2
3
4
5
6
|
{{extend './layout.html'}}
{{block 'content'}}
<div>
<h1>页面默认显示这个内容</h1>
</div>
{{/block}}
|
1
2
3
|
{{include './header.html'}}
<h1> hello {{name}}</h1>
{{include './footer.html'}}
|
静态服务
开放资源:公开指定目录
1
2
3
4
5
6
|
// 去 ./public/ 目录中找对应的资源,推荐使用这种更易辨识的方式
app.use('/public/', express.static('./public'))
// 可以直接通过 /public/xx 的方式访问 public 目录中的所有资源了
// 省略 /public 的方式来访问
app.use(express.static('./public/'))
|
post
1
2
3
4
5
6
7
8
9
10
11
|
var express = require('express')
var bodyParser = require('body-parser')
var app = express()
// 配置 body-parser 中间件(插件,专门用来解析表单 POST 请求)要在 app.use(router) 挂载路由之前
// req 请求对象上会多出一个属性:body。即可通过req.body获取表单中的post请求体数据了
// parse application/xxx.form.urlencoded
app.use(bodyParser.urlencoded({ extended: false }))
// parse application/json
app.use(bodyParser.json())
|
获得 post 的数据
1
2
3
4
5
6
7
8
|
router.post('/students/new', function(req, res){
// 1. 获取表单数据
// 2. 处理 (抽象出单独一个模块专门 CURD)
// 将数据保存到 db.json 中用以持久化
// readFile,parse,push,stringify,writeFile
// 3. 发送响应
console.log(req.body)
})
|
错误处理
1
2
3
|
if(err){
return res.status(500).send('Server error')
}
|
经验积累
json 响应方法
接收一个对象作为参数,自动把对象转为字符串再发送给浏览器
1
2
3
4
|
res.status(200).json({
success: true,
message: 'ok'
})
|
内置中间件
express.static
express.json
express.urlencoded parses incoming requests with URL-encoded payloads
第三方中间件
body-parser
compression
cookie-parser
morgan
response-time
serve-static
session
模块职责清晰而单一
提取路由
将路由放在另一个文件中,exports;在 app.js 中 require
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
// 传统
module.exports = function (app){
app.get('/adas', function(req, res){})
app.get('/adsg', function(req, res){})
app.get('/adas/sfdsd', function(req, res){})
}
// express 提供:专门用来包装路由的方式
// 1. 创建一个路由容器
var router = express.Router()
// 2. 把路由都挂载到 router 路由容器中
router.get('/adas', function(req, res){})
router.get('/adsg', function(req, res){})
router.get('/adas/sfdsd', function(req, res){})
// 3. 把 router 导出
module.exports = router
|
1
2
|
// 把路由容器挂载到 app 服务中
app.use(router)
|
回调函数
1
2
3
4
5
6
7
8
9
10
|
function myDisplayer(some) {
document.getElementById("demo").innerHTML = some;
}
function myCalculator(num1, num2, myCallback) {
let sum = num1 + num2;
myCallback(sum);
}
myCalculator(5, 5, myDisplayer);
|
- 模块化 | 异步
使用 回调函数(获取异步操作结果)
1
2
3
4
5
6
|
Student.updateById(req.body, function(err){
if(err){
return res.status(500).send('Server error')
}
res.redirect('/students')
})
|
定义回调函数
1
2
3
4
5
6
7
8
9
10
11
12
13
|
exports.updateById = function(student, callback){
fs.readFile(dbPath, 'utf8', function(err, data){
if(err){
return callback(err)
}
var students = JSON.parse(data).students
// 需要修改谁,就把谁找出来,ES6 数组方法:find,当某项符合 item.id === students.id 的时候,find 终止遍历,同时返回遍历项。
var stu = students.find(function(item){
return item.id === student.id
})
})
}
|
1
2
3
4
5
6
7
8
9
10
|
exports.find = function(callback){
fs.readFile(dbPath, 'utf8', function(err, data){
if(err){
// 第一个参数,成功是 null,错误是 错误对象
return callback(err)
}
// 第二个参数,成功是 数组,错误是 undefined
callback(null, JSON.parse(data).students)
})
}
|
MongoDB
show dbs
显示所有数据的列表
db
显示当前数据库对象或集合
use 指定要连接的数据库名
若没有则创建
db.students.insertOne({"name": "Jack"})
存的是一个对象
db.students.find()
list
mongoose
基于 MongoDB,官方的包强大但用起来麻烦
1
2
3
4
5
6
7
8
9
10
11
|
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/test', {useNewUrlParser: true, useUnifiedTopology: true});
// 创建一个模型:设计数据库
const Cat = mongoose.model('Cat', { name: String });
// 实例化 Cat
const kitty = new Cat({ name: 'Zildjian' });
// 持久化保存 kitty 实例
kitty.save().then(() => console.log('meow'));
|
使用
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
26
27
28
29
30
31
32
33
34
35
36
|
// 设计 Schema,发布 model
mongoose.connect('mongodb://localhost/itcast') // 没有则创建
// 设计集合结构(表结构),字段名称就是表结构中的属性名称
// 约束的目的是保证数据的完整性,不要有脏数据
var userSchema = new Schema({
title: String,
meta: Number,
comments: [{body: String, date: Date}],
date: {type: Date, default: Date.now},
name:{
type: String,
required: true // 约束
},
gender:{
enum: [0, 1],
default: 0
}
})
// 将文档结构发布为模型
var User = mongoose.model('User', userSchema)
// 第一个参数:首字母大写,表示数据库名称,mongoose 自动将其变为 users 集合名称
// 使用模型构造函数对users集合中的数据进行操作
var admin = new User({
username: 'admin',
password: '123456'
})
admin.save(function(err, ret){
if(err){
console.log('保存失败')
}else{
console.log('保存成功')
}
})
|
1
2
3
4
5
6
|
User.findByIdAndRemove(id, function(err){
if(err){
return res.status(500).send('Server error')
}
res.redirect('/users')
})
|
1
2
3
4
5
6
7
8
9
10
|
User.findOne({ // 还有 find,返回的文档可能不止一个
// 查到&删除数据:User.remove({...})
username: 'zs'
}, function(err, ret){
if(err){
console.log('查询失败')
}else{
console.log('查询成功')
}
})
|
1
|
Model.update(conditions, doc, [options], [callback])
|
根据指定条件更新一个:
1
|
Model.findOneAndUpdate([condition], [update], [options], [callback])
|
根据 id 更新一个
1
2
3
4
5
6
7
8
9
|
User.findByIdAndUpdate('afdafdadsfasf',{
password:'123'
}, function(err, ret){
if(err){
console.log('更新失败')
}else{
console.log('更新成功')
}
})
|
经验&注意
1
2
3
4
5
6
7
8
9
10
|
var userSchema = new Schema({
email: {
type: a + b, // new 的同时已经算好了
required: true
},
creat_time: {
type: Date,
required: Date.now // 如果加(),时间立即创建,就写死了
}
})
|
promise
回调地狱
嵌套回调函数以保证执行顺序,导致
用法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
console.log(1)
// 结果:1243
// Promise 本身不是异步,它里面的任务是异步
// Promise 容器一旦创建,就开始执行里面的代码
new Promise(function(){
console.log(2)
fs.readFile('./a.txt', 'utf8', function(err, data){
if(err){
console.log(err)
// 失败了,把容器的 Pending 状态变为 Rejected
reject(err)
}else{
console.log(3)
console.log(data)
//成功了,把容器的 Pending 状态变为 Resolved
resolve(data)
}
})
})
console.log(4)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
p1
.then(function(data){ // 这个 function 就是 resolve
console.log(data)
// 如果没有return,后面受到的就是undefined
// return 123 // 给了下面一个 then
return p2 // return 一个 Promise 对象,后续的 then 方法的第一个参数作为p2的 resolve
}, function(err){
console.log('读取文件失败')
})
.then(function(data){
// console.log(data) // 是前面 return 123 中的data
console.log(data) // p2 处理 data,顺序保证了
})
|
实战
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
// 按顺序读取文件
var fs = require('fs')
function pReadFile(filePath){
return new Promise(function(resolve, reject){
if(err){
reject(err)
}else{
resolve(data)
}
})
}
pReadFile('./data/a.txt')
.then(function(data){
console.log(data)
return pReadFile('./data/b.txt')
})
.then(function(data){
console.log(data)
return pReadFile('./data/c.txt')
})
.then(function(data){
console.log(data)
})
|
async & await
- The keyword
async
before a function makes the function return a promise
1
2
3
4
5
6
7
|
async function myFunction() {
return "Hello";
}
myFunction().then(
function(value) {myDisplayer(value);},
function(error) {myDisplayer(error);} // 可以没有这一行
);
|
1
2
3
4
|
let hello = async function() { return "Hello" };
hello();
let hello = async () => { return "Hello" };
|
await
only works inside async functions. This can be put in front of any async promise-based function to pause your code on that line until the promise fulfills, then return the resulting value.
jquery ??? 兼容 Promise
1
2
3
4
5
6
7
8
9
|
data = {}
$.get('http://127.0.0.1:3000')
.then(function(user){
data.user = user
return $.get('http://127.0.0.1:3000/jobs')
})
.then(function(jobs){
data.jobs = jobs
})
|
中间件
本质是请求处理方法
消费规则与控制
1
2
3
4
5
6
7
8
9
|
app.use(function(req, res, next){ // 没有路径参数,万能匹配
console.log("1")
next() // 这个函数消费完后,继续往后面走
})
// 严格匹配请求方法和路径的中间件 app.get app.post
app.get('', function(req, res, next){
})
|
错误处理
1
2
3
4
5
6
7
8
9
10
11
12
13
|
app.get('/', function(req, res, next){
fs.readFile('./a/ad', function(err, data){
if(err){
// 当调用 next 的时候,如果传递了参数,则直接往后找到带有四个参数的中间件
next(err)
}
})
})
// 配置错误处理中间件
app.use(function(err, req, res, next){
console.log('app error handler')
})
|
第三方中间件
Express 框架中,默认不支持 Session 和 Cookie,但是可以使用第三方中间件:express-session 来解决:npm install express-session
;在app.use(router)前配置;使用:通过 req.session 来访问和设置 session 成员
1
2
3
4
|
req.session.xxx = 'bar' // 添加
req.session.xxx // 读
req.session.xxx = null // 删除(不严谨)
delete req.session.xxx // 删除(严谨)
|
1
2
3
4
5
|
app.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialize: true
}))
|
其他
中间件,需要经验积累
path 路径操作模块
文件操作同一转换为动态的绝对路径
__dirname 动态获取可以用来获取当前文件模块所属目录的绝对路径
__filename 动态获取可以用来获取当前文件的绝对路径
__dirname 和 __filename 不受执行node命令所属路径影响
拼接路径:path.join()
1
2
|
app.use('/public/', express.static(path.join(__dirname, './public/')))
app.set('views', path.join(__dirname, './views/')) // 默认就是 ./views 目录
|
cheerio
chrome console debug
语法和 jQuary 基本一致,调试是若网页无$
,则引入 jQuary
1
2
3
|
var script = document.createElement("script");
script.src = "https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js";
document.getElementsByTagName('head')[0].appendChild(script);
|
jQuary 学习笔记
特性
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
|
// 入口函数:防止页面还没被渲染出来脚本就执行了
// 1.1、原生Js会等到DOM元素加载完毕,并且图片也加载完毕才会执行;
// 1.2、jQuery会等到DOM元素加载完毕,但不会等到图片加载完毕就会执行。
// 2.1、原生Js如果编写多个入口函数,后面编写的会覆盖前面编写的;
// 2.2、JQuery中编写多个入口函数,后面的不会覆盖前面的。
// 三种写法
$(document).ready(function () { // 文档加载出来以后执行
alert(“入口函数1”);
$('div').width(100).height(100).css('backgroundColor', 'red').text("ada"); // 操作元素,链式编程
});
$(function () {
//文档和图片都加载完成 相当于原生的 window.onload
alert(“JQ的第一个入口”);
});
$(window).ready(function () { //文档和图片全部加载完 执行
alert(“window加载完”);
})
$('.wrap>ul>li').mouseenter(function(){ // mouseover 有冒泡,mouseenter阻止冒泡
// js 一般在前面多一个 on, e.g. onmouseover // mouseout mouseleave
//this是触发了鼠标移动事件的dom对象 // hide() 相当于css('display', 'none')
$(this).children('url').show(); // show() 相当于css('display', 'block')
})
// css 设置多样式,可传入对象
$('#div1').css({
width: 300,
'height': '300px',
'background-color': 'green',
border: '10px solid pink'
})
// 案例:突出展示
$('.swap').find('li').mouseenter(function(){
// $(this).css('opacity', 1) 返回设置这个方法的元素本身
$(this).css('opacity', 1).siblings('li').css('opacity', 0.4);
})
$('.swap').mouseleave(function(){
$(this).find('li').css('opacity', 1);
})
// 案例:手风琴
/*
<li class="menuGroup">
<span class="groupTitle">标题i</span>
<div>我是弹出来的divi</div>
</li>
*/
$('.parentWrap>.menuGroup').click(function(){
// 隐式迭代,链式编程
$(this).children('div').show().parent().siblings('li').children('div'.hide());
})
// 案例:淘宝服饰精品
$('#left>li').mouseenter(function(){
var idx = $(this).index() // 当鼠标移入时,获取这个li标签的索引
$('#center>li').eq(idx).show().siblings('li').hide(); // idx对应的索引显示,其余隐藏
})
|
选择器



Vue 学习笔记
体验
区别与传统 js 的命令式编程,它的编程范式是声明式编程。
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
26
27
28
29
|
<div id="app">
<h2>当前计数:{{counter}}</h2>
<button v-on:click="add">+</button>
<button v-on:click="sub">-</button>
<ul>
<li v-for="item in movies">{{item}}</li>
</ul>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: "#app", // 挂载要管理的元素
data: { // 定义数据
counter: 0,
movies: ['进击的巨人', '龙珠', '天空之城']
},
methods: {
add: funciton(){
console.log('add');
this.counter++
},
sub: function(){
console.log('sub');
this.counter--
}
}
})
</script>
|
前置知识
webpack
yarn
vue in node.js
vuepress
Author
magictomagic
LastMod
2021-01-11
License
Creative Commons Attribution-ShareAlike License