• Node.js项目(一)


    0.前言

    • 架构:前端(Frontend)、后端(Backend)

    • 前端工程化环境:webpack
    • CSS预处理工具:sass
    • JS库(Ajax):jQuery
    • SPA(single page application),路由:SME-Router
    • JS模块化:ES Module,CommonJS Module
    • UI组件库:Bootstrap(AdminLTE)
    • RMVC:art-template


    1.webpack工程化

    • webpack作用:对文件进行打包

    • webpack官网:https://webpack.js.org/

    • 初始化:yarn init -y

    • 添加包:yarn add webpack -D , yarn add webpack-cli -D(本项目使用的webpack版本为4.44.2,webpack5.0版本和4.x版本有很大区别,所以建议使用 yarn add webpack@4.44.2 -D yarn add webpack-cli@3.3.12)

    • 查看帮助:webpack --help

    • webpack 是使用文件进行配置的,所以创建一个配置文件:webpack.config.js

    开始测试:

    webpack.config.js

    const path = require('path')
    
    module.exports = {
        //配置入口
        entry: {
            app: './src/app.js'
        },
    
        //配置出口
        output: {
            //注意必须写物理路径
            path: path.join(__dirname, './dist'),
            filename: 'app.js'
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    src/app.js

    console.log(0)
    
    • 1

    运行:npx webpack

    2.原生webpack

    • 添加webpack原生插件:yarn add html-webpack-plugin@4.4.0 -D

    webpack.config.js添加插件

    const HtmlWebpackPlugin = require('html-webpack-plugin')
    
    module.exports = {
        //...
    
        //配置插件
        plugins: [
            new HtmlWebpackPlugin({
                template: path.join(__dirname, './public/index.html'),
                filename: 'index.html',
                inject: true
            })
    
        ]
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 为了方便启动,我们书写一下脚本:
      "scripts": {
        "build":"npx webpack"
      }
    
    • 1
    • 2
    • 3
    • 使用yarn启动,不用写run:yarn build

    3.配置dev-server

    • 安装dev-server:yarn add webpack-dev-server@3.11.2 -D

    webpack.config.js

    module.exports = {
    
    	//...
        
        //配置dev-server
        devServer: {
            contentBase: path.join(__dirname, 'dist'),
            //是否压缩
            // compress: true,
            port: 8080
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 运行:npx webpack-dev-server -y

    • 为了简化,我们继续写一个脚本: "dev": "npx webpack-dev-server"

    4.文件的拷贝

    • 解决favicon.ico不存在的问题:https://www.lagou.com/images/favicon.ico
    • 保存文件至根目录
    • 安装插件:npm i copy-webpack-plugin@6.4.1 -S -D或者yarn add copy-webpack-plugin -S -D
    • webpack.config.js
    const CopyPlugin = require("copy-webpack-plugin");
    
    module.exports = {
      plugins: [
        new CopyPlugin({
          patterns: [
            //{ from: "source", to: "dest" },
            //{ from: "other", to: "public" },
               from: path.resolve(__dirname, './public/favicon.ico'),
                        to: 'dist'
          ],
        }),
      ],
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 为了方便,我们统一一下版本:

    package.json

    {
      "devDependencies": {
        "@webpack-cli/serve": "^1.7.0",
        "copy-webpack-plugin": "6.4.1",
        "html-webpack-plugin": "4.4.1",
        "webpack": "4.41.5",
        "webpack-cli": "3.3.10",
        "webpack-plugin": "^1.0.5"
      },
      "scripts": {
        "build": "npx webpack",
        "dev": "npx webpack-dev-server"
      },
      "dependencies": {
        "webpack-dev-server": "3.10.1"
      },
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 解决devtools的警告:
    module.exports = {
    
    	//...
        devtool: 'source-map',
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    • 总结:
    const path = require('path')
    const HtmlWebpackPlugin = require('html-webpack-plugin')
    const CopyPlugin = require("copy-webpack-plugin");
    
    module.exports = {
    
        //配置环境
        mode: 'development',
    
        devtool: 'source-map',
    
        //配置入口
        entry: {
            app: './src/app.js'
        },
    
        //配置出口
        output: {
            //注意必须写物理路径
            path: path.join(__dirname, './dist'),
            filename: 'app.js'
    
        },
        //配置插件
        plugins: [
            new HtmlWebpackPlugin({
                template: path.join(__dirname, './public/index.html'),
                filename: 'index.html',
                inject: true
            }),
            new CopyPlugin({
                patterns: [
                    {
                        from: path.resolve(__dirname, './public/favicon.ico'),
                        to: 'dist'
                    },
                ],
            }),
    
        ],
        //配置dev-server
        devServer: {
            contentBase: path.join(__dirname, 'dist'),
            //是否压缩
            // compress: true,
            port: 8080
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    5.art-template模板

    • 1.引用js,css:

    index.html

    DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>lagou-admintitle>
        <link rel="stylesheet" href="./libs/css/bootstrap.min.css">
        
        <link rel="stylesheet" href="./libs/css/font-awesome.min.css">
        
        <link rel="stylesheet" href="./libs/css/ionicons.min.css">
        <link rel="stylesheet" href="./libs/css/AdminLTE.min.css">
        <link rel="stylesheet" href="./libs/css/skin-blue.min.css">
        <link rel="stylesheet" href="./libs/css/blue.css">
    head>
    
    <body class="hold-transition skin-blue sidebar-mini login-page">
        <div id="root">div>
        <script src="./libs/js/jquery-2.2.3.min.js">script>
        <script src="./libs/js/jquery.form.min.js">script>
        <script src="./libs/js/socket.io.js">script>
        <script src="./libs/js/bootstrap.min.js">script>
        <script src="./libs/js/app.min.js">script>
        <script src="./libs/js/icheck.min.js">script>
    body>
    
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 2.引入art-template

    npm i art-template art-template-loader -S -D

    (也可以使用yarn安装)

    • 3.添加新的配置项:

    webpack.config.js

    //这个插件是用来每次打包前先清除原来的内容
    const { CleanWebpackPlugin } = require('clean-webpack-plugin')
    
    module.exports = {
    	//...
    
        //配置loaders
        module: {
            rules: [
                {
                    test: /\.art$/,
                    //排除部分
                    exclude: /(node_modules)/,
                    use: {
                        loader: 'art-template-loader'
                    }
    
                }
            ]
        },
        
        //配置插件
        plugins: [
    		//...
            new CleanWebpackPlugin()
    
        ],
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • index.art是你要准备的网页内容

    src/app.js

    //es6导入模块的方法
    import indexTpl from './views/index.art'
    
    const html = indexTpl({})
    
    $('#root').html(html)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    6.路由(sme-router)

    import SMERouter from 'sme-router'
    //es6导入模块的方法
    import indexTpl from '../views/index.art'
    import signinTpl from '../views/signin.art'
    
    const router = new SMERouter('root')
    
    const htmlIndex = indexTpl({})
    const htmlSignin = signinTpl({})
    
    // $('#root').html(signin)
    
    
    router.route('/', (req, res, next) => {
        res.render(htmlSignin)
    
    })
    
    router.route('/signin', (req, res, next) => {
        res.render(htmlSignin)
    })
    
    export default router
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    src/app.js

    //载入路由
    import router from './routers'
    router.go('/')
    
    • 1
    • 2
    • 3

    7.分离controller层

    src/controller/index.js

    //es6导入模块的方法
    import router from '../routers'
    import indexTpl from '../views/index.art'
    import signinTpl from '../views/signin.art'
    
    const htmlIndex = indexTpl({})
    const htmlSignin = signinTpl({})
    const _handleSubmit = (router) => {
        return (e) => {
            e.preventDefault()
            router.go('/index')
    
        }
    }
    
    const signin = (router) => {
        return (req, res, next) => {
            res.render(htmlSignin)
            $('#signin').on('submit', _handleSubmit(router))
    
        }
    }
    
    const index = (router) => {
        return (req, res, next) => {
            res.render(htmlIndex)
    
        }
    }
    
    export {
        signin,
        index
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    signin.art

    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    routers/index.js

    import SMERouter from 'sme-router'
    const router = new SMERouter('root')
    
    
    import { signin, index } from '../controller'
    
    // $('#root').html(signin)
    
    
    router.route('/', signin(router))
    router.route('/index', index(router))
    
    router.route('/signin', signin(router))
    
    export default router
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    8.css loader

    • 安装:yarn add css-loader@5.0.1 style-loader@2.0.0 -D

    webpack.config.js

    
    module.exports = {
    	//...
    
        //配置loaders
        module: {
            rules: [
                {
                    test: /\.css$/,
                 loaders: ['style-loader', 'css-loader']
    
                }
            ]
        },
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • src/assets/common.css
    html,
    body {
        height: 100%;
    
    }
    
    #root {
        height: 100%;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • controller/index.js
    //...
    const index = (router) => {
        return (req, res, next) => {
            res.render(htmlIndex)
            //window.resize(),让页面撑满整个屏幕
            $(window, '.wrapper').resize()
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 注意插件的版本号

    9.express工程化(backend)

    • 从这里开始步入后端

    • 初始化yarn init -y

    • 添加依赖yarn global add express-generator

    • 初始化express -e

    • 下载依赖:yarn installor npm i

    • 为了方便修改package.json里面的脚本scripts:start:nodemon ./bin/www

    • 启动项目:yarn start

    1.修改路由

    修改app.js

    var createError = require('http-errors');
    var express = require('express');
    var path = require('path');
    var cookieParser = require('cookie-parser');
    var logger = require('morgan');
    
    // var indexRouter = require('./routes/index');
    // var usersRouter = require('./routes/users');
    
    var app = express();
    
    // view engine setup
    app.set('views', path.join(__dirname, 'views'));
    app.set('view engine', 'ejs');
    
    app.use(logger('dev'));
    app.use(express.json());
    app.use(express.urlencoded({ extended: false }));
    app.use(cookieParser());
    app.use(express.static(path.join(__dirname, 'public')));
    
    // app.use('/', indexRouter);
    // app.use('/users', usersRouter);
    const usersRouter = require('./routes/users')
    app.use('/api/users', usersRouter)
    
    // catch 404 and forward to error handler
    app.use(function (req, res, next) {
      next(createError(404));
    });
    
    // error handler
    app.use(function (err, req, res, next) {
      // set locals, only providing error in development
      res.locals.message = err.message;
      res.locals.error = req.app.get('env') === 'development' ? err : {};
    
      // render the error page
      res.status(err.status || 500);
      res.render('error');
    });
    
    module.exports = app;
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    我们这里不需要用到默认的routes/index.js,所以把它删除掉

    修改routes/users.js

    var express = require('express');
    var router = express.Router();
    
    /* GET users listing. */
    router.post('/signup', function (req, res, next) {
      res.send('respond with a resource');
    });
    
    module.exports = router;
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 后端的测试:使用postman或者insomnia测试
      • 测试地址:http://localhost:3000/api/users/signup
      • 请求方式:POST
      • 携带参数:
      • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AsFQogrJ-1668266731497)(D:\Typora\imgs\image-20220829215138044.png)]

    2.controllers

    controllers/users.js

    const signup = (req, res, next) => {
        res.send('hello')
    }
    
    exports.signup = signup
    
    • 1
    • 2
    • 3
    • 4
    • 5

    routes/users.js

    var express = require('express');
    var router = express.Router();
    
    const { signup } = require('../controllers/users')
    
    /* GET users listing. */
    router.post('/signup', signup);
    
    module.exports = router;
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    3.ejs模板

    view/succ.ejs

    {
    "ret": true,
    "errorCode": 0,
    "data":<%- data %>
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    controllers/users.js

    //注册用户
    
    const signup = (req, res, next) => {
    
        const { username, password } = req.body
    
        //这里直接使用模板,第一个参数是模板模板名称succ.ejs
        res.render('succ', {
            data: JSON.stringify({ username, password })
        })
    }
    
    exports.signup = signup
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    4.mongoose

    // getting-started.js
    var mongoose = require('mongoose');
    mongoose.connect('mongodb://localhost/lagou-admin');
    
    • 1
    • 2
    • 3
    • 创建数据库:use lagou-admin

    • kittens demo:

      • db.js
      // getting-started.js
      var mongoose = require('mongoose');
      mongoose.connect('mongodb://localhost/lagou-admin', {
          useNewUrlParser: true,
          useUnifiedTopology: true
      });
      
      var db = mongoose.connection;
      db.on('error', console.error.bind(console, 'connection error:'));
      
      //mongonDB里面的schema就相当于一个模型
      var kittySchema = mongoose.Schema({
          name: String
      });
      
      var Kitten = mongoose.model('Kitten', kittySchema);
      
      var felyne = new Kitten({ name: 'Felyne' });
      felyne.save()
      console.log(felyne.name); // 'Felyne'
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • controllers/users.js
      require('../utils/db')
      //注册用户
      
      const signup = (req, res, next) => {
      
          const { username, password } = req.body
      
          //这里直接使用模板,第一个参数是模板模板名称succ.ejs
          res.render('succ', {
              data: JSON.stringify({ username, password })
          })
      }
      
      exports.signup = signup
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 运行:yarn start
      • 测试:insomnia:

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qzxDqL6Z-1668266731499)(D:\Typora\imgs\image-20220829225000443.png)]

      • 结果:

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pUdXA9zz-1668266731499)(D:\Typora\imgs\image-20220829225020592.png)]

    5.构建自己的schema

    db.js

    //...
    //mongonDB里面的schema就相当于一个模型
    var usersSchema = mongoose.Schema({
        username: String,
        password: String
    });
    
    //集合的名字
    var Users = mongoose.model('users', usersSchema);
    
    exports.Users = Users
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    我们把controller中对db.js的引用删除,提取出M层:model/users.js

    const { Users } = require('../utils/db')
    
    const signup = ({ username, password }) => {
        const users = new Users({
            username,
            password
        })
        users.save()
    }
    
    //下面两种写法是等价的
    // exports.signup = signup
    module.exports = {
        signup
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    controllers/users.js

    const usersModel = require('../models/users')
    //注册用户
    
    const signup = (req, res, next) => {
    
        const { username, password } = req.body
        usersModel.signup({
            username,
            password
        })
    
        //这里直接使用模板,第一个参数是模板模板名称succ.ejs
        res.render('succ', {
            data: JSON.stringify({ username, password })
        })
    }
    
    exports.signup = signup
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    6.同异步问题与注册业务逻辑

    controllers/users.js

    const usersModel = require('../models/users')
    //注册用户
    const signup = async (req, res, next) => {
    
        const { username, password } = req.body
    
        //判断用户是否存在
        let findResult = await usersModel.findUser(username)
        console.log(findResult)
        if (findResult) {
            res.render('fail', {
                data: JSON.stringify({
                    message: '用户名已存在'
                })
            })
    
        } else {
            //数据库里没有这个用户,开始添加用户
            let result = await usersModel.signup({
                username,
                password
            })
            console.log(result)
        }
    
    
    
        //这里直接使用模板,第一个参数是模板模板名称succ.ejs
        res.render('succ', {
            data: JSON.stringify({ username, password })
        })
    }
    
    exports.signup = signup
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    module/users.js

    const { Users } = require('../utils/db')
    
    const findUser = (username) => {
        return Users.findOne({ username })
    
    }
    
    const signup = ({ username, password }) => {
        const users = new Users({
            username,
            password
        })
        return users.save()
    
    }
    
    //下面两种写法是等价的
    exports.signup = signup
    exports.findUser = findUser
    // module.exports = {
    //     signup
    // }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    views/fail.ejs

    {
    "ret": true,
    "errorCode": -1,
    "data":<%- data %>
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    7.密码加密-bcrypt

    • 安装:yarn add bcrypt

    • 创建工具js:

    utils/tools.js

    const bcrypt = require('bcrypt')
    
    exports.hash = (myPlaintextPassword) => {
    
        return new Promise((resolve, reject) => {
            bcrypt.hash(myPlaintextPassword, 10, function (err, hash) {
    
                if (err) {
                    reject(err)
                }
                resolve(hash)
            })
        });
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    controllers/users.js

    
    const { hash } = require('../utils/tools')
    //注册用户
    const signup = async (req, res, next) => {
            //设置返回头
        res.set('content-type', 'application/json; charset=utf-8')
    
        //...
        //加密后的密码
        const bcryptPassword = await hash(password)
    
        //...
        } else {
            //数据库里没有这个用户,开始添加用户
            let result = await usersModel.signup({
                username,
                password: bcryptPassword
            })
    
        }
    	//...
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • bcryptAPI:
      • hash(data,salt,cb)

      • salt:salt - [REQUIRED] - the salt to be used to hash the password. if specified as a number then a salt will be generated with the specified number of rounds and used

      • rounds=8 : ~40 hashes/sec
        rounds=9 : ~20 hashes/sec
        rounds=10: ~10 hashes/sec
        rounds=11: ~5  hashes/sec
        rounds=12: 2-3 hashes/sec
        rounds=13: ~1 sec/hash
        rounds=14: ~1.5 sec/hash
        rounds=15: ~3 sec/hash
        rounds=25: ~1 hour/hash
        rounds=31: 2-3 days/hash
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10

    8.跨域访问CORS(backend)

    • 安装插件:yarn add cors

    • app.js中使用中间件

    var cors = require('cors')
    
    app.use(cors())
    
    
    • 1
    • 2
    • 3
    • 4

    10.跨域访问webpack(frontend)

    webpack.config.js

    
    module.exports = {
    	//...
        //配置dev-server
        devServer: {
      	//...
            port: 8080,
            proxy: {
                '/api': {
                    target: 'http://localhost:3000',
                    //pathRewrite: { "^/api": "" }
                }
            }
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 配置完要重启一遍,因为配置文件已经修改了

    11.获取用户列表

    1.backend

    • 路由:routes/users.js
    //...
    router.get('/list', list)
    
    • 1
    • 2
    • 控制层:controllers/users.js
    //...
    //用户列表
    const list = async (req, res) => {
            res.set('content-type', 'application/json; charset=utf-8')
        const listResult = await usersModel.findList()
        res.render('succ', {
            data: JSON.stringify( listResult )
        })
    }
    
    exports.list = list
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • M层:modules/users.js
    //...
    const findList = () => {
        //排序
        return Users.find().sort({ _id: -1 })
    }
    
    exports.findList = findList
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2.frontend

    • views/users-list.art
    {{each data}}
    
      {{$index+1}}.
      {{$value.username}}
      
    
    {{/each}}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • controllers/index.js
    import usersListTpl from '../views/users-list.art'
    
    //...
    const _list = () => {
        $.ajax({
            url: '/api/users/list',
            success(result) {
                 $('#users-list').html(usersListTpl({
                    data: result.data
                }))
                
                //console.log(result)
            }
        })
    }
    
    const index = (router) => {
        return (req, res, next) => {
            //渲染首页
            res.render(htmlIndex)
            //window.resize(),让页面撑满整个屏幕
            $(window, '.wrapper').resize()
    
            //填充用户列表
            $('#content').html(usersTpl())
    
            //渲染list
            _list()
    
    
            //点击保存,提交表单
            $('#users-save').html(usersTpl())
    
    
        }
    }
    
    const _signup = () => {
        const $btnClose = $('#users-close')
        //提交表单
        const data = $('#users-form').serialize()
        console.log(data)
    
        $.ajax({
            url: '/api/users/signup',
            type: 'post',
            data,
            success(res) {
                console.log(res)        
                //刷新
                _list()
            }
        })
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    12.分页(frontend)

    • views/users-pages.art
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • controllers/index.js
    
    import userListPageTpl from '../views/users-pages.art'
    
    //...
    
    const pageSize = 5
    let dataList = []
    
    const _loadData = () => {
        return $.ajax({
            url: '/api/users/list',
            async: false,
            success(result) {
                dataList = result.data
                //分页
                _pagination(result.data)
            }
        })
    }
    const _pagination = (data) => {
    
        const total = data.length
        //往上取整
        const pagesCount = Math.ceil(total / pageSize)
        const pageArray = new Array(pagesCount)
    
        const htmlPage = userListPageTpl({
            pageArray
        })
    
        $('#users-page').html(htmlPage)
        //默认添加第一个
        $('#users-page-list li:nth-child(2').addClass('active')
        $('#users-page-list li:not(:first-child , :last-child)').on('click', function () {
            $(this).addClass('active').siblings().removeClass('active')
            // console.log($(this).index())
            _list($(this).index())
        })
    
    }
    
    const _list = (pageNo) => {
    
        let start = (pageNo - 1) * pageSize
        $('#users-list').html(usersListTpl({
            //截取数据
            data: dataList.slice(start, start + pageSize)
        }))
    
    }
    
    
    const index = (router) => {
        return async (req, res, next) => {
            //渲染首页
            res.render(htmlIndex)
            //window.resize(),让页面撑满整个屏幕
            $(window, '.wrapper').resize()
    
            //填充用户列表
            $('#content').html(usersTpl())
    
            //初次渲染list
            _loadData()
            _list(1)
    
    
            //点击保存,提交表单
            $('#users-save').html(usersTpl())
    
        }
    }
    
    const _signup = () => {
        const $btnClose = $('#users-close')
    
        //提交表单
        const data = $('#users-form').serialize()
        console.log(data)
    
        $.ajax({
            url: '/api/users/signup',
            type: 'post',
            data,
            sucdess: async (res) => {
                console.log(res)
                //提交数据后渲染
                _loadData()
                _list(1)
            }
        })
        //单击关闭模拟框
        $btnClose.click()
    
    }
    //...
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96

    13.修改路由

    backend/routes/users.js

    var express = require('express');
    var router = express.Router();
    
    const { signup, list } = require('../controllers/users')
    
    /* GET users listing. */
    router.post('/', signup);
    router.get('/', list)
    // router.delete('/', remove)
    
    module.exports = router;
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    frontend/controllers/index.js

    //es6导入模块的方法
    // import router from '../routers'
    import indexTpl from '../views/index.art'
    import signinTpl from '../views/signin.art'
    import usersTpl from '../views/users.art'
    import usersListTpl from '../views/users-list.art'
    import userListPageTpl from '../views/users-pages.art'
    
    
    const htmlIndex = indexTpl({})
    const htmlSignin = signinTpl({})
    const pageSize = 5
    let dataList = []
    const _handleSubmit = (router) => {
        return (e) => {
            e.preventDefault()
            router.go('/index')
    
        }
    }
    
    const _loadData = () => {
        return $.ajax({
            url: '/api/users',
            async: false,
            success(result) {
                dataList = result.data
                //分页
                _pagination(result.data)
            }
        })
    }
    const _pagination = (data) => {
    
        const total = data.length
        //往上取整
        const pagesCount = Math.ceil(total / pageSize)
        const pageArray = new Array(pagesCount)
    
        const htmlPage = userListPageTpl({
            pageArray
        })
    
        $('#users-page').html(htmlPage)
        //默认添加第一个
        $('#users-page-list li:nth-child(2').addClass('active')
        $('#users-page-list li:not(:first-child , :last-child)').on('click', function () {
            $(this).addClass('active').siblings().removeClass('active')
            // console.log($(this).index())
            _list($(this).index())
        })
    
    }
    
    const _list = (pageNo) => {
    
        let start = (pageNo - 1) * pageSize
        $('#users-list').html(usersListTpl({
            //截取数据
            data: dataList.slice(start, start + pageSize)
        }))
    
    }
    
    
    
    const signin = (router) => {
        return (req, res, next) => {
            res.render(htmlSignin)
            $('#signin').on('submit', _handleSubmit(router))
    
        }
    }
    
    const index = (router) => {
        return async (req, res, next) => {
            //渲染首页
            res.render(htmlIndex)
            //window.resize(),让页面撑满整个屏幕
            $(window, '.wrapper').resize()
    
            //填充用户列表
            $('#content').html(usersTpl())
    
            //初次渲染list
            _loadData()
            _list(1)
    
    
            //点击保存,提交表单
            $('#users-save').html(usersTpl())
    
        }
    }
    
    const _signup = () => {
        const $btnClose = $('#users-close')
    
        //提交表单
        const data = $('#users-form').serialize()
        console.log(data)
    
        $.ajax({
            url: '/api/users',
            type: 'post',
            data,
            sucdess: async (res) => {
                console.log(res)
                //提交数据后渲染
                _loadData()
                _list(1)
            }
        })
        //单击关闭模拟框
        $btnClose.click()
    
    }
    
    const signup = () => {
    
    }
    
    
    
    export {
        signin,
        index
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
  • 相关阅读:
    【300+精选大厂面试题持续分享】大数据运维尖刀面试题专栏(十六)
    20个实用Python自动化脚本技巧 + 推荐:《Python网络爬虫入门到实战》宝典
    如何对振弦式渗压计进行数据读取和处理
    有趣的前端面试题
    LeetCode刷题复盘笔记——37. 解数独(一文搞懂回溯解决经典的解数独问题)
    sentinel----隔离和降级
    CentOSPython使用openpyxl、pandas读取excel报错
    红酒煮水果可以吗?
    【Sprig AOP】
    shell通配符与glob
  • 原文地址:https://blog.csdn.net/m0_50315078/article/details/127827168