preface
This state is a bit like writing a composition when going to school, the beginning is always “pull” not to come out, suffocating uncomfortable.
The original address
The source address
From the background
After the separation of the front and back ends, the front end will need to deal with some node layer work, such as template rendering, interface forwarding, part of the business logic, etc. The commonly used frameworks are KOA and KOA-Router, etc.
Now we need to implement a requirement:
- The user to access
/fe
When pages are displayedhello fe - The user to access
/backend
When pages are displayedhello backend
Are you thinking, this requires me to use koA, KOA-Router, native Node module can do it.
const http = require('http')
const url = require('url')
const PORT = 3000
http.createServer((req, res) = > {
let { pathname } = url.parse(req.url)
let str = 'hello'
if (pathname === '/fe') {
str += ' fe'
} else if (pathname === '/backend') {
str += ' backend'
}
res.end(str)
}).listen(PORT, () => {
console.log(`app start at: ${PORT}`)})Copy the code
It is true that using a framework may seem wasteful for very simple requirements, but there are drawbacks to the above implementation, such as
- We need to resolve the path ourselves.
- Path parsing and logic writing are coupled together. If more and more complex requirements need to be implemented in the future, that’s fine.
So let’s try using KOA and KoA-Router
app.js
const Koa = require('koa')
const KoaRouter = require('koa-router')
const app = new Koa()
const router = new KoaRouter()
const PORT = 3000
router.get('/fe', (ctx) => {
ctx.body = 'hello fe'
})
router.get('/backend', (ctx) => {
ctx.body = 'hello backend'
})
app.use(router.routes())
app.use(router.allowedMethods())
app.listen(PORT, () => {
console.log(`app start at: ${PORT}`)})Copy the code
The path resolution is handled by the KOA-router, but the overall writing is still a bit problematic.
- There is no way to reuse anonymous functions
- Routing configuration and logical processing in one file, no separation, big project, is also a hassle.
Let’s optimize it a little bit by looking at the overall directory structure
├─ App.js // App.js // App.js // App.js // App.js // App.js // App.js // App.js // App.js // App.js // App.js // App.js // App.js // App.js // App.js // App.js // App.js // App.js ├── Routes // │ ├─ index. Js ├─ views // Template ├─ indexCopy the code
Preview the logic of each file
The intersection of app.js apps
const Koa = require('koa')
const middleware = require('./middleware')
const app = new Koa()
const PORT = 3000
middleware(app)
app.listen(PORT, () => {
console.log(`app start at: ${PORT}`)})Copy the code
Js Routes /index.js Route configuration center
const KoaRouter = require('koa-router')
const router = new KoaRouter()
const koaCompose = require('koa-compose')
const hello = require('.. /controller/hello')
module.exports = (a)= > {
router.get('/fe', hello.fe)
router.get('/backend', hello.backend)
return koaCompose([ router.routes(), router.allowedMethods() ])
}
Copy the code
Controller /hello.js Logic of the Hello module
module.exports = {
fe (ctx) {
ctx.body = 'hello fe'
},
backend (ctx) {
ctx.body = 'hello backend'}}Copy the code
Middleware /index.js Middleware unified registry
const routes = require('.. /routes')
module.exports = (app) = > {
app.use(routes())
}
Copy the code
You may be wondering at this point, right?
A simple requirement looks too complicated by this. Is it necessary?
The answer is: yes, this directory structure may not make the most sense, but the routing, controller, view layer and so on all have their place. Great help for future extensions.
I don’t know if you noticed the routing configuration
Js Routes /index.js Route configuration center
const KoaRouter = require('koa-router')
const router = new KoaRouter()
const koaCompose = require('koa-compose')
const hello = require('.. /controller/hello')
module.exports = (a)= > {
router.get('/fe', hello.fe)
router.get('/backend', hello.backend)
return koaCompose([ router.routes(), router.allowedMethods() ])
}
Copy the code
Each route corresponds to a controller to process, very separate, very common ah!! This seems to be a common configuration pattern for vue-router or react-router on the front end.
But when there are more modules, this folder will become
const KoaRouter = require('koa-router')
const router = new KoaRouter()
const koaCompose = require('koa-compose')
// Now you need to require the files of each module
const hello = require('.. /controller/hello')
const a = require('.. /controller/a')
const c = require('.. /controller/c')
module.exports = (a)= > {
router.get('/fe', hello.fe)
router.get('/backend', hello.backend)
// Configure the routing and controller for each module
router.get('/a/a', a.a)
router.post('/a/b', a.b)
router.get('/a/c', a.c)
router.get('/a/d', a.d)
router.get('/c/a', c.c)
router.post('/c/b', c.b)
router.get('/c/c', c.c)
router.get('/c/d', c.d)
/ /... , etc.
return koaCompose([ router.routes(), router.allowedMethods() ])
}
Copy the code
Is there any way to register a koA-router without manually introducing one controller after another and then manually calling the get/POST method of the KOA-Router?
For example, we only need to do the following configuration to complete the above manual configuration function.
routes/a.js
module.exports = [
{
path: '/a/a'.controller: 'a.a'
},
{
path: '/a/b'.methods: 'post'.controller: 'a.b'
},
{
path: '/a/c'.controller: 'a.c'
},
{
path: '/a/d'.controller: 'a.d'}]Copy the code
routes/c.js
module.exports = [
{
path: '/c/a'.controller: 'c.a'
},
{
path: '/c/b'.methods: 'post'.controller: 'c.b'
},
{
path: '/c/c'.controller: 'c.c'
},
{
path: '/c/d'.controller: 'c.d'}]Copy the code
Then use the pure-koa-Router module for a simple configuration
const pureKoaRouter = require('pure-koa-router')
const routes = path.join(__dirname, '.. /routes') // Specify a route
const controllerDir = path.join(__dirname, '.. /controller') // Specify the root directory of the controller
app.use(pureKoaRouter({
routes,
controllerDir
}))
Copy the code
This way we can focus on routing configuration instead of manually requiring a bunch of files.
Briefly introduce the above configuration
{
path: '/c/b'.methods: 'post'.controller: 'c.b'
}
Copy the code
Path: Path configuration, can be string /c/b, array [‘/c/b’], of course, can be regular expression /\c\b/
Methods (string get, array [‘get’, ‘post’], default get,
Controller: logical processing method for matching routes. C.b indicates method b for exporting c files in controllerDir. A.B.C indicates method c for exporting b files in /a/b of controllerDir
The source code to achieve
Let’s step through the implementation logic
You can click on the source code
The overall structure
module.exports = ({ routes = [], controllerDir = ' ', routerOptions = {} }) = > {
// xxx
return koaCompose([ router.routes(), router.allowedMethods() ])
})
Copy the code
Pure – koa – the router receives
routes
- Const routes = path.join(__dirname, ‘.. /routes’))
- Const routes = path.join(__dirname, ‘..) const routes = path.join(__dirname, ‘.. /routes/tasks.js’)
- Const routes = require(‘.. /routes/index’))
controllerDir
, root directory of the controllerrouterOptions
New KoaRouter, you can see thatkoa-router
This package is then executed to return the koaCompose wrapped middleware for the KOA instance to add.
Parameter adaptation
assert(Array.isArray(routes) || typeof routes === 'string'.'routes must be an Array or a String')
assert(fs.existsSync(controllerDir), 'controllerDir must be a file directory')
if (typeof routes === 'string') {
routes = routes.replace('.js'.' ')
if (fs.existsSync(`${routes}.js`) || fs.existsSync(routes)) {
// Handle the file passed in
if (fs.existsSync(`${routes}.js`)) {
routes = require(routes)
// Process the incoming directory
} else if (fs.existsSync(routes)) {
// Read the files in the directory and merge them
routes = fs.readdirSync(routes).reduce((result, fileName) = > {
return result.concat(require(nodePath.join(routes, fileName)))
}, [])
}
} else {
// routes If it is a string it must be the path of a file or directory
throw new Error('routes is not a file or a directory')}}Copy the code
Routing registered
Whether routes are passed in a file or a directory, or exported directly, the configuration is structured like this
Routes Content Preview
[
// Basic configuration
{
path: '/test/a'.methods: 'post'.controller: 'test.index.a'
},
// Multiple routes to one controller
{
path: [ '/test/b'.'/test/c'].controller: 'test.index.a'
},
// Multiple routes to multiple controllers
{
path: [ '/test/d'.'/test/e'].controller: [ 'test.index.a'.'test.index.b']},// Single route pair controller
{
path: '/test/f'.controller: [ 'test.index.a'.'test.index.b']},/ / regular
{
path: /\/test\/\d/.controller: 'test.index.c'}]Copy the code
Take the initiative to register
let router = new KoaRouter(routerOptions)
let middleware
routes.forEach((routeConfig = {}) = > {
let { path, methods = [ 'get' ], controller } = routeConfig
// Routing method type parameter adaptation
methods = (Array.isArray(methods) && methods) || [ methods ]
// Controller parameter adaptation
controller = (Array.isArray(controller) && controller) || [ controller ]
middleware = controller.map((controller) = > {
// 'test.index.c' => [ 'test', 'index', 'c' ]
let controllerPath = controller.split('. ')
// Method name c
let controllerMethod = controllerPath.pop()
try {
// Read /test/index file c method
controllerMethod = require(nodePath.join(controllerDir, controllerPath.join('/')))[ controllerMethod ]
} catch (error) {
throw error
}
// controllerMethod must be a method
assert(typeof controllerMethod === 'function'.'koa middleware must be a function')
return controllerMethod
})
// Finally use router.register to register
router.register(path, methods, middleware)
Copy the code
The implementation process of the source code basically ends here.
At the end
Pure-koa-router separates route configuration from controller, allowing us to focus on route configuration and controller implementation. I hope I can help you a little.
The original address
The source address