ES6 实战

151 阅读3分钟

前端启动

  • yarn install
  • yarn dev

后端启动

  • rollup -c -w
  • nodemon ./dist/bundle.js

后端 - node 框架

Koa

  • express
    • Express 是一个保持最小规模的灵活的 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}!`);
});
  • koa
    • Koa 是一个新的 web 框架,由 Express 幕后的原班人马打造, 致力于成为 web 应用和 API 开发领域中的一个更小、更富有表现力、更健壮的基石。 通过利用 async 函数,Koa 帮你丢弃回调函数,并有力地增强错误处理。 Koa 并没有捆绑任何中间件, 而是提供了一套优雅的方法,帮助您快速而愉快地编写服务端应用程序。
    • async / await
    • 中间件需要第三方引用;
  • restana
  • nest (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 作为我的构建的配置;
  • -wwatch 监听文件的变化,实时构建;

中间件

redux-compose

function(arr) {
    return arr.reduce((a, b) => (...args) => a(b(...args)) )
}

svelte

问题

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 规范?

  1. 利用编译工具运行 esm,比如课程中的 Rollup 的方式;
  2. 使用一些工具直接运行,比如 babel-node
  3. 使用 mjs 作文文件后缀;
  4. package.json 中配置 module

5. 说一下你理解的 svelte?

  • 开放性问题。
  • 可以考虑从:编译体积、编译期的工作以及语法规范这几个方面进行回答。