koa2/Express4+mongdb+webpack4优化配置实践经验

本文用于自我总结以及给予新手一些借鉴借此共勉

demo地址 => 传送门

ps:觉得有帮助的同学点个赞或者github star 在此谢过

使用版本:

  • koa: 2.6.1

  • express: 4.X

  • webpack: 4.19.1

  • mongoose: 5.3.9

实现的功能:

 批量上传和下载

 mongoose数据库操作

 增删查

 webpack4部分优化配置

单文件下载&批量下载:

  tips:批量下载需要使用引入archiver第三方包将多个文件打包成一个压缩文件

  • Koa:
    //此处分为文件和图片两个接口
    router.get('/downloadFile/:name',//文件
        async ctx => {
            ctx.set('Content-disposition', `attachment;filename=${ctx.params.name}`);
            const paths = `src/assets/upload/files/${ctx.params.name}`
    
            const data = fs.createReadStream(paths) // 发送路径文件内容的文件
    
            ctx.body = data
        }
    ).get('/downloadImg/:name',//图片
        async ctx => {
            ctx.set('Content-disposition', `attachment;filename=${ctx.params.name}`);
            const paths = `src/assets/upload/imgs/${ctx.params.name}`;
    
            ctx.attachment(paths);
            await send(ctx, paths);
        }
    )
    
    //批量下载
    router.get('/batchDownload',
        async ctx => {
            const list = [{ name: '1.jpg', path: 'src/assets/upload/imgs/1.jpg' }, { name: '1.csv', path: 'src/assets/upload/files/1.csv' }, { name: '2.png', path: 'src/assets/upload/imgs/2.png' }];//name为压缩文件内生成的文件名称,path为要下载的文件路径
            const zipName = 'download.zip';//自定义生成的压缩文件名称
            const zipPath = `src/assets/download/${zipName}`;//自定义生成的压缩文件路径
            const zipStream = fs.createWriteStream(zipPath);
            const zip = archiver('zip');
            zip.pipe(zipStream);
    
            for (let i = 0; i < list.length; i++) {
                // 添加单个文件到压缩包
                zip.append(fs.createReadStream(list[i].path), { name: list[i].name })
            }
            await zip.finalize();
            ctx.attachment(zipPath);
            await send(ctx, zipPath);
        }
    )
  • Express:
    //此处同Koa
    app.get('/downloadFile/:name',
        (req, res) => {
            const filePath = `src/assets/upload/files/${req.params.name}`
            if (fs.existsSync(filePath)) {
                res.download(filePath)
            } else {
                res.json({ err: 'file is not exist', success: false })
            }
        }
    ).get('/downloadImg/:name',
        (req, res) => {
            const filePath = `src/assets/upload/imgs/${req.params.name}`
            if (fs.existsSync(filePath)) {
                res.download(filePath)
            } else {
                res.json({ err: 'file is not exist', success: false })
            }
        }
    ).get('/batchDownload',
        async (req, res) => {
            const list = [{ name:'1.jpg',path: 'src/assets/upload/imgs/1.jpg' }, { name:'1.csv',path: 'src/assets/upload/files/1.csv' }, { name:'2.png',path: 'src/assets/upload/imgs/2.png' }];
            const zipName = 'download.zip';
            const zipPath = `src/assets/download/${zipName}`;
            const zipStream = fs.createWriteStream(zipPath);
            const zip = archiver('zip');
            zip.pipe(zipStream);
            
            for (let i = 0; i < list.length; i++) {
                // 添加单个文件到压缩包
                zip.append(fs.createReadStream(list[i].path), { name: list[i].name })
            }
            await zip.finalize();
            setTimeout(() => {
                res.download(zipPath)
            }, 0);
            
        }
    )

上传&批量上传:

  • Koa:
    //接收post请求需要先处理options请求
    app.use(
        async (ctx, next) => {
            if (ctx.request.method === "OPTIONS") {
                ctx.response.status = 200
                ctx.set('Access-Control-Allow-Origin', ctx.request.headers.origin)
                ctx.set("Access-Control-Max-Age", 24 * 60 * 60 * 1000);
                ctx.set("Access-Control-Allow-Methods", 'GET,POST,OPTIONS,DELETE,PUT');
                ctx.set("Access-Control-Allow-Headers", 'Origin,Content-Type,Authorization,Accept,X-Custom-Header,anonymous,X-Requested-With')
                ctx.body = ''
            } else {
                await next()
            }
    
        }
    )
    //上传
    router.post('/upload',
        ctx => {
            try {
                const files = ctx.request.files.file
                files.length ?
                    files.forEach(file =>
                        fs
                            .createReadStream(file.path)
                            .pipe(fs.createWriteStream(`${file.type.indexOf('image') >= 0 ? 'src/assets/upload/imgs' : 'src/assets/upload/files'}/${file.name}`))
                    ) :
                    fs
                        .createReadStream(files.path)
                        .pipe(fs.createWriteStream(`${files.type.indexOf('image') >= 0 ? 'src/assets/upload/imgs' : 'src/assets/upload/files'}/${files.name}`))
    
                ctx.body = '上传成功'
            } catch (e) {
                ctx.body = '上传失败'
            }
        }
    )
  • Express(需要引入multer接受upload文件):
      const multer = require('multer')
     const upload = multer({ dest: '/src/upload' })

    //
    处理options app.all('*', (req, res, next) => { if (req.method === "OPTIONS") { res.status(200) res.end() } else { next() } } ).post('/upload', upload.any(), (req, res, next) => { try { const files = req.files console.log(files) files.length ? files.forEach(file => fs .createReadStream(file.path) .pipe(fs.createWriteStream(`${file.mimetype.indexOf('image') >= 0 ? 'src/assets/upload/imgs' : 'src/assets/upload/files'}/${file.originalname}`)) ) : fs .createReadStream(files.path) .pipe(fs.createWriteStream(`${files.mimetype.indexOf('image') >= 0 ? 'src/assets/upload/imgs' : 'src/assets/upload/files'}/${files.originalname}`)) res.json({ err: 'upload success', success: false }) } catch (e) { res.json({ err: 'upload failed', success: false }) } } )

mongodb线上数据mLab库:

  分为两个文件 connect.js连接数据库,db.js导出mongodb的自定义数据模型

  • connect.js
    const mongoose = require('mongoose')
    
    mongoose.Promise = global.Promise
    
    mongoose.connect('mongodb://用户名:密码@ds145093.mlab.com:45093/用户名',
        { useNewUrlParser: true },
        (err, info) => {
            if (err) throw 'mongodb connect failed'
            console.log('mongodb connect success')
        }
    )
  • db.js
    require('./connect')
    const mongoose = require('mongoose');
    const Schema = mongoose.Schema;
    
    const todoSchema = new Schema({
        name: String,
        isDoing: Boolean,
        createDate: Date, 
    }, {versionKey: false})
    
    
    const Todo = mongoose.model('Todo', todoSchema)
    exports.Todo=Todo;

增删查:

  • Koa:
    //需要引入koa-body
    const koaBody = require('koa-body')
    
    //查询所有记录
    app.get('/todoList',
        async ctx => {
            try {
                let data = await Todo.find({})
                ctx.body = { data, success: true }
            } catch (e) {
                ctx.body = { data: [], success: false }
            }
    
        },
    ).get('/todoList/:name',//模糊查询单一记录
        async ctx => {
            const params = { name: { $regex: new RegExp(`${ctx.params.name}`) } }
    
            try {
                let data = await Todo.find(params)
                ctx.body = { data, success: true }
            } catch (e) {
                ctx.body = { data: [], success: false }
            }
        }
    ).post('/todoList', koaBody(),//新增记录
        async ctx => {
            const todo = new Todo({ ...ctx.request.body })
            try {
                let data = await todo.save()
                ctx.body = { data, success: true }
            } catch (err) {
                ctx.body = { data: { err }, success: false }
            }
    
        }
    ).del('/todoList/:name',//删除记录
        async ctx => {
            const params = { name: ctx.params.name }
            try {
                let data = await Todo.findOneAndDelete(params)
                ctx.body = { data, success: true }
            } catch (err) {
                ctx.body = { data: { err }, success: false }
            }
        }
    )
  • Express:
    //查询所有记录
    app.get('/todoList',
        async (req, res, next) => {
            try {
                let data = await Todo.find({})
                res.json({ data, success: true })
            } catch (e) {
                res.json({ data: [], success: false })
            }
    
        },
    ).get('/todoList/:name',//模糊查询记录
        async (req, res, next) => {
            const params = { name: { $regex: new RegExp(`${req.params.name}`) } }
    
            try {
                let data = await Todo.find(params)
                res.json({ data, success: true })
            } catch (e) {
                res.json({ data: [], success: false })
            }
        }
    ).post('/todoList',//新增记录
        async (req, res, next) => {
            const todo = new Todo({ ...req.body })
            try {
                let data = await todo.save()
                res.json({ data, success: true })
            } catch (err) {
                res.json({ data: { err }, success: false })
            }
    
        }
    ).delete('/todoList/:name',//删除记录
        async (req, res, next) => {
            const params = { name: req.params.name }
            try {
                let data = await Todo.findOneAndDelete(params)
                res.json({ data, success: true })
            } catch (err) {
                res.json({ data: { err }, success: false })
            }
        }
    )

webpack优化配置:

  • 打包第三方库预编译:
    //dll.js
    const path = require('path')
    const webpack = require('webpack')
    
    module.exports = {
      entry: {
        vendor: [
            'react',
            'react-dom',
            'react-router-dom',
            'immutable', 
            'antd',
            'axios',      
        ],
      },
      output: {
        filename: '[name].dll.js',
        path: path.resolve(__dirname, '../public'),
        library: '[name]_library', 
      },
      plugins: [
        new webpack.DefinePlugin({
          'process.env.NODE_ENV': JSON.stringify('production')
        }),
        new webpack.DllPlugin({
          path: path.resolve(__dirname, '../public','[name]-manifest.json'),
          name: '[name]_library',
          context: __dirname
        })
      ],
    }
  • 提取公共代码
    optimization: {
        splitChunks: {
          chunks: 'all',
          name: false,
          cacheGroups: {
            commons: {
              name: 'commons',
              priority: 10,
              chunks: 'initial'
            }
          },
        }
      }
  • happypack
    //引入happypack
    const HappyPack = require('happypack')
    
    new HappyPack({
          id: 'jsx',
          threads: 4,
          loaders: ['babel-loader?presets[]=react,presets[]=latest&compact=false'],
    })
  • 藉由路由分包
    //引入react-loadable
    import Loadable from 'react-loadable'
    
    const MyLoadingComponent = ({ isLoading, error }) => {
      return ''
    }
    const Nav = Loadable({
      loader: () => import(/* webpackChunkName: "Nav" */'../components/Nav'),//注释是为了让webpack打包出来的文件以路由为名
      loading: MyLoadingComponent
    })
  • 以上的优化足够一般的项目使用,还有其他一些零散的优化配置如减少编译后文件大小:Tree-shaking等,缓存loader: cache-loader,文件hash缓存:webpack-md5-plugin等 有兴趣的可以自行Google或百度

以上就是所有内容 有不明白的可以评论 看到的会回复

猜你喜欢

转载自www.cnblogs.com/zyktbs/p/10183843.html
今日推荐