共计 13095 个字符,预计需要花费 33 分钟才能阅读完成。
## MongoDB 封装 CRUD 办法
CRUD 是指创立 (Create)、读取(Read)、更新(Update) 和删除 (Delete) 四种根本的数据处理操作。
在软件开发中,CRUD 办法通常用于对数据库或其余存储系统中的数据进行操作。
具体来说,CRUD 办法包含以下四种操作:
- 创立(Create):向数据库或其余存储系统中插入新数据。
- 读取(Read):从数据库或其余存储系统中获取数据。
- 更新(Update):更新数据库或其余存储系统中已有的数据。
- 删除(Delete):从数据库或其余存储系统中删除数据。
这些操作能够通过编写相应的程序实现,以便在应用程序中对数据进行解决和治理。
例如,在一个商店的库存管理系统中:
- 应用 CRUD 办法能够新增商品(Create)、
- 查问某个商品的库存数量(Read)、
- 批改商品的价格和库存数量(Update)
- 从库存中删除商品(Delete)。
更多精彩内容,请 微信搜寻“前端爱好者
“, 戳我 查看。
实现步骤
首先,须要装置相应的依赖项,包含 koa,koa-router,koa-bodyparser 和 mongoose。
能够通过在终端中运行以下命令进行装置:
npm install koa koa-router koa-bodyparser mongoose
而后,在我的项目中创立一个 db.js 文件,封装 MongoDB 的连贯和操作方法:
const mongoose = require('mongoose');
// 连贯数据库
mongoose.connect('mongodb://localhost/mydb', { useNewUrlParser:true})
.then(() => console.log('MongoDB connected'))
.catch(err => console.error(err));
// 定义数据模型
const userSchema = new mongoose.Schema({
name: String,
age: Number,
email: String
});
const User = mongoose.model('User', userSchema);
// 封装 CRUD 操作
module.exports = {
// 获取所有用户数据
async getUsers() {return await User.find();
},
// 新增用户数据
async addUser(data) {const user = new User(data);
return await user.save();},
// 依据 ID 获取用户数据
async getUserById(id) {return await User.findById(id);
},
// 依据 ID 更新用户数据
async updateUserById(id, data) {return await User.findByIdAndUpdate(id, data);
},
// 依据 ID 删除用户数据
async deleteUserById(id) {return await User.findByIdAndDelete(id);
}
};
在下面的代码中,咱们定义了 User 数据模型,并在 getUsers()、addUser()、getUserById()、updateUserById() 和 deleteUserById() 办法中封装了 MongoDB 的 CRUD 操作。
接下来,须要在我的项目的入口文件中创立一个 Koa 利用,并在路由中应用上述封装的办法:
const Koa = require('koa');
const Router = require('koa-router');
const bodyParser = require('koa-bodyparser');
const db = require('./db');
const app = new Koa();
const router = new Router();
// 解析申请体
app.use(bodyParser());
// 获取所有用户数据
router.get('/users', async (ctx) => {const users = await db.getUsers();
ctx.body = users;
});
// 新增用户数据
router.post('/users', async (ctx) => {const { name, age, email} = ctx.request.body;
const user = await db.addUser({name, age, email});
ctx.body = user;
});
// 依据 ID 获取用户数据
router.get('/users/:id', async (ctx) => {const { id} = ctx.params;
const user = await db.getUserById(id);
ctx.body = user;
});
// 依据 ID 更新用户数据
router.put('/users/:id', async (ctx) => {const { id} = ctx.params;
const {name, age, email} = ctx.request.body;
const user = await db.updateUserById(id, { name, age, email});
ctx.body = user;
});
// 依据 ID 删除用户数据
router.delete('/users/:id', async (ctx) => {const { id} = ctx.params;
await db.deleteUserById(id);
ctx.status = 204;
});
app.use(router.routes());
app.listen(3000);
在下面的代码中,咱们创立了一个 Koa 利用,并在路由中应用封装的 CRUD 办法解决对应的 HTTP 申请。其中,通过 koa-bodyparser 中间件解析申请体,应用 :id 对 ID 进行占位符解决。
最初,通过在终端中运行以下命令启动利用:
node app.js
即可通过拜访 http://localhost:3000/users 等 API 进行 MongoDB 的 CRUD 操作。
案例:用户模块 CRUD 封装 革新
在 controller 文件夹 下,新建 utils 文件夹,并且新建index.js 文件
// controller/utils/index.js
/**
* 用于增加数据的公共办法
* @param {*} model
* @param {*} params
* @param {*} ctx
* @returns
*/
const add = (model, params, ctx) => (model.create(params).then(rel => {if (rel) {
ctx.body = {
code: 200,
msg: '增加胜利',
data: rel
}
} else {
ctx.body = {
code: 300,
msg: '增加失败'
}
}
}).catch(err => {
ctx.body = {
code: 400,
msg: '增加时出现异常'
}
console.error(err)
})
)
/**
* 用于批改数据的公共办法
* @param {*} model
* @param {*} where
* @param {*} params
* @param {*} ctx
* @returns
*/
const update = (model, where, params, ctx) => (model.updateOne(where, params).then(rel => {
ctx.body = {reslut: rel}
}).catch(err => {
ctx.body = {
code: 400,
msg: '批改时出现异常'
}
console.error(err)
})
)
/**
* 用于删除的公共办法
* @param {*} model
* @param {*} where
* @param {*} ctx
* @returns
*/
const del = (model, where, ctx) => (model.findOneAndDelete(where).then(rel => {
ctx.body = {reslut: rel}
}).catch(err => {
ctx.body = {
code: 400,
msg: '删除时出现异常'
}
console.error(err)
})
)
/**
* 用于查问所有数据的公共办法
* @param {*} model
* @param {*} where
* @param {*} ctx
* @returns
*/
const find = (model, where, ctx) => (model.find(where).then(rel => {
ctx.body = {result: rel}
}).catch(err => {
ctx.body = {
code: 400,
msg: '查问时出现异常'
}
console.error(err)
})
)
/**
* 查问单条数据的公共办法
* @param {*} model
* @param {*} where
* @param {*} ctx
* @returns
*/
const findOne = (model, where, ctx) => (model.findOne(where).then(rel => {
ctx.body = {result: rel}
}).catch(err => {
ctx.body = {
code: 400,
msg: '查问时出现异常'
}
console.error(err)
})
)
module.exports = {
find,
findOne,
add,
update,
del
}
在 controller 文件夹 下,批改 user 文件夹
// controller/user.js
const {User} = require('../models')
const crud = require('./crudUtil')
// 增加零碎用户
const userAdd = async (ctx) => {let {username = '',pwd =''} = ctx.request.body
await crud.add(User,{username,pwd},ctx)
}
// 批改用户
const userUpdate = async (ctx) => {
let params = ctx.request.body
await crud.update(
User,
{_id:params._id},
{username:params.username,pwd:params.pwd},
ctx
)
}
// 删除用户
const userDel = async (ctx) => {let {_id} = ctx.request.body
await crud.del(User,{_id},ctx)
}
// 查问所有用户
const userFind = async (ctx) => {await crud.find(User,null,ctx)
}
// 查问单个用户
const userFindOne = async (ctx) => {await crud.findOne(User,{_id:ctx.params.id},ctx)
}
module.exports = {
userAdd,
userUpdate,
userDel,
userFind,
userFindOne
}
即可实现用户模块 CRUD 封装 革新。
koa2 跨域问题
跨域(Cross-Origin)指的是在浏览器中运行的脚本试图拜访不同源(Protocol、域名、端口)的资源,即在不同源之间进行数据交互。
当 JavaScript 代码尝试向不同源的服务器发动申请时,浏览器会收回“跨域申请”(Cross-Origin Request)并受到浏览器的同源策略限度。
同源策略是浏览器为了保障用户的信息安全而施行的一种安全策略,它要求浏览器限度从脚本收回的跨域 HTTP 申请,只有在同源的状况下才容许互相通信。
同源指协定、域名和端口都雷同。如果两个 URL 的协定、域名或端口其中之一不同,就被认为是不同源。
跨域问题在 Web 开发中比拟常见,在开发基于 AJAX 和 RESTful API 的利用时尤其须要留神。
解决跨域的办法有很多种,包含 JSONP、CORS、反向代理、WebSocket 等。
具体抉择哪种办法取决于理论利用场景和需要。
koa 解决 跨域问题
设置 HTTP 响应头
在 Koa2 中解决 CORS 跨域问题的罕用办法是增加一个中间件来设置 HTTP 响应头。
具体实现如下:
const Koa = require('koa');
const app = new Koa();
app.use(async (ctx, next) => {
// 容许跨域的域名,* 代表所有域名都能够跨域拜访
ctx.set('Access-Control-Allow-Origin', '*');
// 容许的申请办法
ctx.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
// 容许的申请头字段
ctx.set('Access-Control-Allow-Headers', 'Content-Type, Authorization');
if (ctx.method === 'OPTIONS') {
// 解决预检申请,间接返回 204 状态码,示意容许跨域拜访
ctx.status = 204;
} else {
// 持续解决失常申请
await next();}
});
// 在这里注册路由处理程序
app.use(router.routes());
app.listen(3000);
在下面的代码中,咱们创立了一个中间件来设置 HTTP 响应头,包含 Access-Control-Allow-Origin(容许跨域的域名)、Access-Control-Allow-Methods(容许的申请办法)和 Access-Control-Allow-Headers(容许的申请头字段)。
同时,咱们也解决了预检申请 OPTIONS,如果以后申请办法是 OPTIONS,则间接返回 204 状态码示意容许跨域拜访;否则,持续解决失常申请。
请留神,为了不便起见,在下面的示例中 将 Access-Control-Allow-Origin 设置为了 *
,示意容许所有域名进行跨域拜访。在理论利用中,咱们 应该依据需要设置具体的容许跨域域名
。
koa2-cors 库
在 Koa2 中解决跨域问题能够应用 koa2-cors 库。
上面是一个简略的示例:
首先,装置 koa2-cors
:
npm install koa2-cors --save
而后,在应用程序中应用这个中间件:
const Koa = require('koa');
const cors = require('koa2-cors');
const app = new Koa();
// 配置跨域选项
app.use(cors({
origin: '*', // 容许哪些源拜访,默认为 *
maxAge: 86400, // 预检申请缓存工夫(秒),默认为 5 秒
credentials: true, // 是否携带身份验证信息,默认为 false
allowMethods: ['GET', 'HEAD', 'PUT', 'POST', 'DELETE', 'PATCH'], // 容许哪些 HTTP 办法,默认为 GET,HEAD,PUT,POST,DELETE,PATCH
allowHeaders: ['Content-Type', 'Authorization', 'Accept'], // 容许哪些 HTTP 头部,默认为 Content-Type,Authorization,Accept
exposeHeaders: ['WWW-Authenticate', 'Server-Authorization'] // 容许哪些自定义的 HTTP 头部裸露给浏览器,默认为空数组
}));
// 增加路由处理器
app.use(async (ctx, next) => {
const name = ctx.query.name || 'World';
ctx.body = {message: `Hello, ${name}!` };
});
app.listen(3000, () => {console.log('Server is running at http://localhost:3000');
});
在下面的代码中,咱们导入了 koa2-cors 中间件,并将其作为第一个中间件应用。在配置选项中,咱们指定了容许哪些源拜访,以及是否携带身份验证信息等。这里的配置选项能够依据须要进行批改。
留神,koa2-cors 中间件必须放在其余中间件之前,否则会导致跨域申请失败。在本例中,咱们增加了一个简略的路由处理器,在响应中返回一条音讯,以便测试跨域申请是否起作用。
应用 koa2-cors 中间件能够不便地解决 Koa2 应用程序中的跨域问题。
每日一课:ES6 新增属性 第三局部
ES6 (ECMAScript 2015) 引入了许多新的语法和性能个性,其中一些新增的属性包含:
对象和数组的新办法:
ES6 中为对象和数组提供了很多新的办法,它们能够使开发者更加高效地操作数据。
上面是这些新办法的介绍和利用示例:
对象的新办法
Object.assign
Object.assign 办法用于将多个源对象的属性复制到指标对象中。它返回指标对象自身。
const target = {a: 1};
const source1 = {b: 2};
const source2 = {c: 3};
Object.assign(target, source1, source2);
console.log(target); // {a: 1, b: 2, c: 3}
下面的例子中,咱们申明了一个指标对象 target 和两个源对象 source1 和 source2,应用 Object.assign 办法将两个源对象的属性复制到指标对象中。
须要留神的是,Object.assign 是 浅拷贝
,只复制对象的属性值。如果属性值是一个对象,则只是复制了该对象的援用,而不是进行深拷贝。
Object.keys、Object.values 和 Object.entries
- Object.keys 办法返回一个数组,蕴含对象所有可枚举的属性名称。
- Object.values 办法返回一个数组,蕴含对象所有可枚举的属性值。
- Object.entries 办法返回一个数组,蕴含对象所有可枚举的属性键值对。
const obj = {a: 1, b: 2, c: 3};
console.log(Object.keys(obj)); // ['a', 'b', 'c']
console.log(Object.values(obj)); // [1, 2, 3]
console.log(Object.entries(obj)); // [[ 'a', 1], ['b', 2], ['c', 3] ]
下面的例子中,咱们应用了 Object.keys、Object.values 和 Object.entries 办法别离获取了对象 obj 中所有属性名称、属性值和属性键值对。
须要留神的是,这些办法只返回可枚举的属性。
Object.getOwnPropertyDescriptors
Object.getOwnPropertyDescriptors 办法返回一个对象,形容了一个对象所有本身属性的属性描述符。
const obj = {a: 1};
console.log(Object.getOwnPropertyDescriptors(obj));
/*
{
a: {
value: 1,
writable: true,
enumerable: true,
configurable: true
}
}
*/
下面的例子中,咱们应用了 Object.getOwnPropertyDescriptors 办法获取了对象 obj 本身属性的所有属性描述符。
须要留神的是,这个办法返回的对象蕴含了所有属性的描述符,而不仅限于可枚举的属性。
数组的新办法
Array.from
Array.from 办法用于将类数组对象和可迭代对象转换为真正的数组。
const arr1 = Array.from('hello');
const arr2 = Array.from([1, 2, 3], x => x * 2);
console.log(arr1); // ['h', 'e', 'l', 'l', 'o']
console.log(arr2); // [2, 4, 6]
下面的例子中,咱们将一个字符串和一个可迭代对象转换为了真正的数组。
在第二个示例中,咱们还演示了如何应用箭头函数对数组元素进行转换。
Array.of
Array.of 办法用于创立一个新的、具备可变数量的参数的数组实例。
const arr1 = Array.of(1, 2, 3);
const arr2 = Array.of(4);
console.log(arr1); // [1, 2, 3]
console.log(arr2); // [4]
下面的例子中,咱们应用 Array.of 办法创立了两个数组实例。
Array.prototype.includes
Array.prototype.includes 办法用于判断一个数组是否蕴含某个指定的值。
const arr = [1, 2, 3];
console.log(arr.includes(2)); // true
console.log(arr.includes(4)); // false
下面的例子中,咱们应用 Array.prototype.includes 办法判断数组 arr 是否蕴含值 2 和值 4。
须要留神的是,这个办法不会辨别 +0 和 -0,NaN 和 NaN 等非凡状况。
Array.prototype.find 和 Array.prototype.findIndex
- Array.prototype.find 办法用于返回数组中满足条件的第一个元素,如果没有找到,则返回 undefined。
- Array.prototype.findIndex 办法用于返回数组中满足条件的第一个元素的索引,如果没有找到,则返回 -1。
const arr = [1, 2, 3];
const result1 = arr.find(x => x > 1);
const result2 = arr.findIndex(x => x > 1);
console.log(result1); // 2
console.log(result2); // 1
在下面的例子中,咱们应用 Array.prototype.find 和 Array.prototype.findIndex 办法找到了数组 arr 中第一个大于 1 的元素和它的索引。
须要留神的是,这两个办法都承受一个回调函数作为参数,用于定义搜寻条件。
Proxy 和 Reflect 对象
ES6 中提供了 Proxy 和 Reflect 两个内置对象,它们能够帮忙开发者更灵便地 管制对象的行为 和实现元编程。
Proxy 对象
Proxy 对象容许你创立一个代理对象,用来拦挡对象的各种操作,比方拜访一个属性、调用一个函数等。通过代理对象,咱们能够齐全管制原始对象的行为,实现本人的逻辑。
上面是一个简略的示例,应用 Proxy 对象对一个对象进行拦挡:
const obj = {
name: 'Alice',
age: 18,
};
const handler = {get(target, key) {console.log(`Getting ${key}`);
return target[key];
},
set(target, key, value) {console.log(`Setting ${key} to ${value}`);
target[key] = value;
},
};
const proxy = new Proxy(obj, handler);
console.log(proxy.name); // Getting name, Alice
proxy.age = 20; // Setting age to 20
在下面的示例中,咱们定义了一个对象 obj,而后应用 Proxy 对象创立了一个代理对象 proxy。
咱们还定义了一个处理器对象 handler,其中实现了 get 和 set 两个拦截器函数。
当咱们通过代理对象 proxy 拜访某个属性时,会触发 get 拦截器,并输入相应的日志;当咱们通过代理对象 proxy 批改某个属性时,会触发 set 拦截器,并输入相应的日志。
须要留神的是,Proxy 对象不反对间接遍历,如果要实现遍历,须要把代理对象转换成一个数组或一个 Set 对象。
Reflect 对象
Reflect 对象提供了一组静态方法,用于操作对象。
这些办法与原来的全局办法、函数或操作符等性能相似,但具备更对立的函数签名和返回值,更易于应用和学习。
上面是一些罕用的 Reflect 办法:
Reflect.get(target, propertyKey[, receiver])
读取指标对象的指定属性的值。如果指定属性不存在,则返回 undefined。
const obj = {name: 'Alice'};
const value = Reflect.get(obj, 'name');
console.log(value); // Alice
Reflect.set(target, propertyKey, value[, receiver])
设置指标对象的指定属性的值。如果指定属性不存在,则创立它。
const obj = {name: 'Alice'};
Reflect.set(obj, 'age', 18);
console.log(obj); // {name: 'Alice', age: 18}
Reflect.has(target, propertyKey)
判断指标对象是否存在指定属性。
const obj = {name: 'Alice'};
const result1 = Reflect.has(obj, 'name');
const result2 = Reflect.has(obj, 'age');
console.log(result1); // true
console.log(result2); // false
Reflect.deleteProperty(target, propertyKey)
从指标对象中删除指定属性。
const obj = {name: 'Alice', age: 18};
Reflect.deleteProperty(obj, 'age');
console.log(obj); // {name: 'Alice'}
Reflect.construct(target, argumentsList[, newTarget])
用指定的参数列表创立一个对象。相当于应用 new 关键字调用函数。
function Person(name, age) {
this.name = name;
this.age = age;
}
const args = ['Alice', 18];
const obj = Reflect.construct(Person, args);
console.log(obj); // {name: 'Alice', age: 18}
须要留神的是,Reflect 对象并不反对通过 call 或 apply 办法调用。
Symbol 类型
ES6 引入了一种新的根本数据类型 Symbol,它示意一个举世无双的值,能够用作对象属性的键名,防止键名抵触。
Symbol 类型,用于创立惟一的标识符,并反对在对象中定义非字符串的属性键名。
一个 Symbol 值是通过 Symbol() 函数创立的,每次调用该函数生成的值都是惟一的,因为它在外部生成了一个举世无双的标识符。
上面是一个简略的示例,演示了如何创立和应用 Symbol 类型:
const sym1 = Symbol();
const sym2 = Symbol();
console.log(sym1); // Symbol()
console.log(sym2); // Symbol()
console.log(sym1 === sym2); // false
const obj = {[sym1]: 'value1',
[sym2]: 'value2',
};
console.log(obj[sym1]); // value1
console.log(obj[sym2]); // value2
在下面的示例中,咱们首先创立了两个不同的 Symbol 值,而后通过方括号语法将它们别离作为对象 obj 的属性名,并设置相应的属性值。
须要留神的是,因为 Symbol 值是举世无双的,因而即便两个 Symbol 值看起来雷同,但它们也是不同的,不能相互代替。
除了传递空参外,Symbol() 函数还能够传递一个字符串参数,用于形容 Symbol 值,但这并不会影响 Symbol 值的唯一性。
能够应用 Symbol.for() 办法创立一个可共享的 Symbol 值,这样多个执行环境也能够共享同一个 Symbol 值。
// 在以后执行环境中创立一个 Symbol 值
const sym1 = Symbol('mySymbol');
// 在全局注册表中创立一个 Symbol 值
const sym2 = Symbol.for('mySymbol');
console.log(sym1); // Symbol(mySymbol)
console.log(sym2); // Symbol(mySymbol)
console.log(sym1 === sym2); // false
console.log(Symbol.keyFor(sym1)); // undefined
console.log(Symbol.keyFor(sym2)); // mySymbol
在下面的示例中,咱们首先应用 Symbol() 函数创立了一个 Symbol 值 sym1,并传入了一个形容字符串 “mySymbol”。
而后,咱们应用 Symbol.for() 办法创立了一个 Symbol 值 sym2,也传入了雷同的形容字符串 “mySymbol”。
只管两者形容字符串雷同,但它们是两个不同的 Symbol 值,因而 sym1 和 sym2 不相等。
同时,咱们还通过 Symbol.keyFor() 办法查看了 sym1 和 sym2 对应的键名,发现只有 sym2 有一个名为 “mySymbol” 的键名,因而 sym1 对应的键名返回了 undefined。
须要留神的是,Symbol.for() 办法创立的 Symbol 值会保留在全局注册表中,在不同的执行环境中能够被共享。
因而,如果多个执行环境中都应用了雷同的 Symbol.for() 参数字符串,它们将共享同一个 Symbol 值。
如果想要在注册表中查找某个 Symbol 值对应的键名,在 ES6 中能够应用 Symbol.keyFor() 办法。
此外,ES6 还提供了一些内置的 Symbol 值,比方 Symbol.iterator、Symbol.hasInstance、Symbol.species 等,这些值被用于实现各种特定的性能。
Generator 函数和迭代器
ES6 引入的 Generator 函数是一种用于创立可暂停执行的函数,并且反对返回多个值的语法。
与一般函数不同,调用 Generator 函数并不会立刻执行该函数体代码,而是返回一个迭代器对象,通过迭代器的 next() 办法来管制函数的执行。
Generator 函数定义的语法如下:
function* gen() {
yield 1;
yield 2;
yield 3;
}
const g = gen();
console.log(g.next()); // {value: 1, done: false}
console.log(g.next()); // {value: 2, done: false}
console.log(g.next()); // {value: 3, done: false}
console.log(g.next()); // {value: undefined, done: true}
在下面的示例中,咱们定义了一个 gen Generator 函数,并在外部通过 yield 关键字返回了三个值。
调用 gen() 函数返回的是一个迭代器对象 g,通过 g.next() 办法来一一遍历 yield 返回的值。
须要留神的是,当函数所有的 yield 均已执行完后,再次调用 g.next() 的返回值为 { value: undefined, done: true},批示迭代器曾经完结。
Generator 函数能够通过 return() 办法手动终止迭代器,并设置返回值。同时,Generator 函数还能够承受内部参数,并将其传递到外部应用,以管制函数的执行。
除了 手动调用 next() 办法 外,能够应用 for…of 循环来遍历 Generator 函数返回的迭代器对象中的值。须要留神的是,在循环完结后,return() 办法设置的返回值并不会被打印进去,因而要想获取该值,应该在循环内手动调用 return() 办法,并设置返回值。
总之,ES6 的 Generator 函数和迭代器为 JavaScript 减少了一种弱小而灵便的编程形式,能够更加不便和高效地治理简单的异步逻辑。