前端启动
yarn installyarn dev
后端启动
rollup -c -wnodemon ./dist/bundle.js
后端 - node 框架
Koa
expressExpress是一个保持最小规模的灵活的Node.js Web应用程序开发框架,为Web和移动应用程序提供一组强大的功能。- 回调函数;
- 内置了很多中间件;
/*
express 基本用法
*/
const express = require('express');
const app = express();
const PORT = 3001;
app.get('/', (req, res) => {
res.send('Hello World!!!');
});
app.listen(PORT, () => {
console.log(`Example app listening on port ${PORT}!`);
});
koaKoa是一个新的web框架,由Express幕后的原班人马打造, 致力于成为web应用和API开发领域中的一个更小、更富有表现力、更健壮的基石。 通过利用async函数,Koa帮你丢弃回调函数,并有力地增强错误处理。Koa并没有捆绑任何中间件, 而是提供了一套优雅的方法,帮助您快速而愉快地编写服务端应用程序。async / await;- 中间件需要第三方引用;
restananest (next.js, nuxt.js)
洋葱模型
-
中间件(
middleware / compose):redux的中间件reduce();koa的中间件dispatch递归函数;axios:[ ] req -> unshift(); res -> push()
-
express
const express = require('express');
const app = express();
const PORT = 3001;
app.use((req, res, next) => {
console.log('querying start level 1');
next();
console.log('querying end level 1');
});
app.use((req, res, next) => {
console.log('querying start level 2');
next();
console.log('querying end level 2');
});
app.use((req, res, next) => {
console.log('querying start level 3');
next();
console.log('querying end level 3');
});
/*
输出顺序(中间件的执行顺序 - 洋葱模型):
querying start level 1
querying start level 2
querying start level 3
querying end level 3
querying end level 2
querying end level 1
*/
app.get('/', (req, res) => {
res.send('Hello World!!!');
});
app.listen(PORT, () => {
console.log(`Example app listening on port ${PORT}!`);
});
koa
基本写法
const Koa = require('koa');
const app = new Koa();
const PORT = 3002;
const main = ctx => {
ctx.body = 'hello Koa';
};
app.use(main);
app.listen(PORT, () => {
console.log(`server app listening on port ${PORT}!`);
});
koa-router
/*
koa-router 基本用法
*/
const Koa = require('koa');
const Router = require('koa-router');
const app = new Koa();
const router = new Router();
const PORT = 3002;
const main = ctx => {
ctx.body = 'hello Koa';
};
app.use(main);
router.get('/', (ctx, next) => {
ctx.type = 'application/json';
ctx.body = {
data: 'home',
};
});
router.get('/api', (ctx, next) => {
ctx.type = 'application/json';
ctx.body = {
data: 'hello api',
};
});
app.use(router.routes());
app.use(router.allowedMethods());
app.listen(PORT, () => {
console.log(`server app listening on port ${PORT}!`);
});
文件抽离:
// index.js 文件
// const Koa = require('koa');
// const Router = require('koa-router');
// 使用 esm 规范,在 node 环境下直接运行报错,首先要使用编译工具编译,然后运行编译完的文件
import Koa from 'koa';
import Router from 'koa-router';
import Movie from './movie';
import User from './user';
const app = new Koa();
const router = new Router();
const PORT = 3002;
[Movie, User].forEach(route => {
app.use(route.routes());
app.use(route.allowedMethods());
});
app.listen(PORT, () => {
console.log(`server app listening on port ${PORT}!`);
});
// movie.js 文件
import Router from 'koa-router';
const router = new Router();
router.get('/movie', async (ctx, next) => {
ctx.body = 'movie';
});
export default router;
// movie.js 文件
import Router from 'koa-router';
const router = new Router();
router.get('/user', async (ctx, next) => {
ctx.body = 'user';
});
export default router;
项目代码
做一个装饰器
- 路径:
back_end\src\utils\decorator.js
export const RequestMethod = {
GET: 'get',
POST: 'post',
PUT: 'put',
DELETE: 'delete',
OPTION: 'option',
PATCH: 'patch',
};
export const controllersArray = [];
// 装饰器对类的行为的改变,是代码编译时发生的,而不是运行时;
// 所以,在之前就干了这么一件事
// movieController --> movieController.prefix = "/movie"
export function Controller(prefix = '') {
return function (target) {
// 类装饰器的参数:当前类
target.prefix = prefix;
};
}
export function RequestMapping(method = '', url = '') {
return function (target, methodName, descriptor) {
let path = '';
if (!url) {
path = `/${methodName}`;
} else {
path = url;
}
// 创建 router 需要的数据
const item = {
url: path,
method,
handler: target[methodName],
constructor: target.constructor,
};
controllersArray.push(item);
};
}
controller
movieController- 路径:
back_end\src\controller\movieController.js
- 路径:
import { Controller, RequestMapping, RequestMethod } from '../utils/decorator';
import data from '../../mock/data';
@Controller('/movie')
export default class MovieController {
@RequestMapping(RequestMethod.GET, '/all')
async getAllMovie(ctx) {
// ctx.body = 'movie';
const [key, page] = ctx.querystring.split('=');
let resultData = data._embedded.episodes;
ctx.body = {
data: resultData.slice((page - 1) * 10, page * 10 - 1), // 分页处理
count: resultData.length, // 总条数
};
}
@RequestMapping(RequestMethod.GET)
async id(ctx) {
ctx.body = 'getAllMovie';
}
}
userController- 路径:
back_end\src\controller\userController.js
- 路径:
import { Controller, RequestMapping, RequestMethod } from '../utils/decorator';
@Controller('/user')
export default class MovieController {
@RequestMapping(RequestMethod.GET, '/all')
async getUser(ctx) {
ctx.body = 'user';
}
}
index.js
import Movie from './movieController';
import User from './userController';
export default { Movie, User };
index 主文件
- 路径:
back_end\src\index.js
import Koa from 'koa';
import Router from 'koa-router';
import bodyParser from 'koa-bodyparser';
import R from './controller/index'; // 这里面引入的目的:避免 rollup 把 controller 给 tree shaking 掉
import { controllersArray } from './utils/decorator';
const PORT = 3003;
const app = new Koa();
const router = new Router();
// cors 跨域处理
app.use(async (ctx, next) => {
ctx.set('Access-Control-Allow-Origin', '*');
ctx.set('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Accept');
ctx.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
ctx.set('Content-Type', 'application/json;charset=utf-8');
if (ctx.request.method.toLowerCase() === 'options') {
ctx.stale = 200;
} else {
await next();
}
});
app.use(bodyParser());
controllersArray.forEach(item => {
let { url, method, handler, constructor } = item;
const { prefix } = constructor;
if (prefix) url = `${prefix}${url}`;
router[method](url, handler);
});
app.use(router.routes());
app.listen(PORT, () => {
console.log(`server app listening on port ${PORT}!`);
});
前端
svelte(react / vue)rollup(webpack)smelte(antd / element)tailwind CSS (bootstrap / css / postcss)
后端
Sequelize(java-mybatis)
其他
Rollup
rollup -c -w
-c:找根目录的rollup.config.js作为我的构建的配置;-w:watch监听文件的变化,实时构建;
中间件
redux-compose
function(arr) {
return arr.reduce((a, b) => (...args) => a(b(...args)) )
}
svelte
svelte网址:www.sveltejs.cn/tutorial/ba…
问题
1. ES 的装饰器有哪些?
-
类装饰器
- 可以用来装饰整个类
@testable
class MyTestableClass {
// ...
}
function testable(target) {
target.isTestable = true;
}
MyTestableClass.isTestable; // true
- 方法、属性装饰器
class Person {
@readonly
name() {
return `${this.first} ${this.last}`;
}
}
// 装饰器第一个参数是类的原型对象,上例是 Person.prototype,装饰器的本意是要“装饰”类的实例,但是这个时候实例还没生成,所以只能去装饰原型(这不同于类的装饰,那种情况时 target 参数指的是类本身);第二个参数是所要装饰的属性名,第三个参数是该属性的描述对象。
2. 为什么装饰器不能用于函数?
装饰器只能用于类和类的方法,不能用于函数,因为存在函数提升。
另一方面,如果一定要装饰函数,可以采用高阶函数的形式直接执行。
3. 请实现一个 koa-compose
function compose(args) {
let result;
return function (ctx) {
let i = 0;
let dispatch = function (i, ctx) {
let fn;
if (i < args.length) fn = args[i];
// console.log(ctx)
if (i === args.length) {
result = ctx;
return;
}
return fn(ctx, dispatch.bind(null, ++i));
};
dispatch(0, ctx);
return result;
};
}
// 测试:
function discount(ctx, next) {
console.log('starting discount..');
next(ctx * 0.8); // 八折优惠
console.log('ending discount..');
}
function express(ctx, next) {
console.log('starting express..');
next(ctx + 12); // 不包邮,12元运费
console.log('ending express..');
}
function num(ctx, next) {
console.log('starting num..');
next(ctx * 10); // 一共十个
console.log('ending num..');
}
const c = compose([num, discount, express]);
console.log(c(15));
4. 如何在 node 环境下使用 esm 规范?
- 利用编译工具运行
esm,比如课程中的Rollup的方式; - 使用一些工具直接运行,比如
babel-node; - 使用
mjs作文文件后缀; - 在
package.json中配置module;
5. 说一下你理解的 svelte?
- 开放性问题。
- 可以考虑从:编译体积、编译期的工作以及语法规范这几个方面进行回答。