万字好文,手把手教你做一个「vue3+express+typescript」的全栈模板项目
一、写在前面
在很久很久之前,「前端开发」和「切图工具人」是基本画等号的。但随着 Node.js 的发展,前端可以做全栈的工作了,极大地提升了开发效率(再也不用和后端 battle 接口字段了,真香!)。
本篇文章记录了做「vue3+express+typescript」全栈模板项目的全过程。按照这篇文章一步一步操作,你就能够搭建出一个属于自己的全栈模板项目,非常适合各位前端同学食用。
本项目已经开源到 GitHub 上,各位看官给个 Star 再走呗:https://github.com/shadowings-zy/vue-express-ts-template-project
二、技术选型
2-1、主要用到的依赖
- 编程语言:typescript
- 包管理工具:pnpm
- 前端框架:vue3(配套使用 vuex 和 vue-router)
- 打包工具:webpack
- 后端框架:express
- 部署工具:pm2
2-2、选择这些依赖的原因
编程语言选择typescript
,这主要是因为相比于javascript
,typescript
提供了诸如类型、枚举、接口、类、继承等高级特性,而这些特性对于构建维护大型项目是十分有帮助的。
前端框架选用了 vue3 全家桶,这是因为我更喜欢像 vue 这种官方提供了与之配套的基础设施(比如vuex
和vue-router
)的前端框架,而像react
这种选router
都要在好几个router
之间调研一下的框架,我就不那么喜欢了。当然,调整一下打包配置,我们完全可以把vue
换成react
。
打包工具选了webpack
,老牌且功能强大的打包工具。而没有选vite
之类的打包工具主要还是可扩展性的考虑,如果你想把vue
无缝切换成其他前端框架,尤其是一些小众一点的框架,那么使用vite
很可能会踩坑,甚至可能都找不到如何迁移。
包管理工具选了pnpm
,这主要是因为pnpm
相比于其他包管理工具,有着更快的安装速度和更小的node_modules
体积,还是很香的。
后端框架选了express
,不必多说,功能强大的主流后端框架,无脑选就 ok 了。
部署工具选了pm2
,它能很方便的守护nodejs
进程,也支持自动重启等特性,是服务器上跑express
服务必备的东西。
三、初始化项目
3-1、monorepo
在这个模板项目中,我们会以monorepo
方式管理前端项目和后台项目,也就是说,它们都会被放到同一个代码仓库中,所以我们新建一个package
目录,并在该目录下新增client
和server
目录,分别放前端和后端的代码。
3-2、初始化 npm
然后,执行npm init
命令,生成该项目的package.json
,生成的内容如下:
// 路径: package.json{"name": "vue-express-ts-template-project","description": "vue express ts template project","author": "shadowings-zy","license": "ISC"
}
3-3、初始化 git
再执行git init
命令,初始化 git 仓库。
3-4、初始化 typescript
然后,执行pnpm add typescript -D
命令,安装typescript
的依赖。依赖安装好之后,我们在项目根目录下新建一个tsconfig.json
文件,该文件用于配置typescript
,具体内容如下:
// 路径: tsconfig.json{"compilerOptions": {"lib": ["ES2019", "dom"], // 编译引入的库"target": "ES2019", // 编译目标"module": "commonjs" // 模块类型},"exclude": ["./node_modules"] // 不包含的文件
}
3-5、当前目录结构
我们现在的目录结构如下:
.
├── package
│ ├── client # 前端代码
│ └── server # 后端代码
├── package.json # npm配置
└── tsconfig.json # typescript配置
做完这些初始化的操作后,我们项目的基础结构就有了,接下来,就是愉快的开发时间了。
四、后端部分
本部分会介绍如何一步一步地搭建一个express
后端服务。
4-1、开发准备
后端首先执行pnpm add express
命令安装express
,再执行pnpm add @types/express
命令安装对应的类型声明文件。
安装好依赖后,我们需要调整tsconfig.json
,我们可以在package/server
目录下建一个目录级的tsconfig.json
,用于控制server
目录下的typescript
文件,具体内容如下:
// 路径: package/server/tsconfig.json{"extends": "../../tsconfig.json", // 继承根目录的tsconfig"compilerOptions": {"outDir": "../../output", // 编译后的js文件地址"moduleResolution": "node", // 模块解析策略"esModuleInterop": true // 支持按照es6模块规范导入commonjs模块},"include": ["./**/*.ts"], // 包含的文件"exclude": ["../../node_modules"] // 不包含的文件
}
4-2、开发后端服务
下面就进入到了本文中最重要的一部分——开发后端服务了。
我们先简单分析一下后端服务要提供的功能:
- 1、渲染 html 页面,并提供打包好的前端静态资源(js、css 等)。
- 2、提供 API 接口供前端访问。
那么针对这两个功能,我们就可以选择对应的中间件去处理了:
- 使用
express.static
进行静态资源加载,并结合compression
提供的 gzip 功能,进一步压缩产物体积。 - 使用
express.Router
将请求路由到对应的controller
并进行处理。
4-2-1、静态资源加载
首先我们在package/server
目录下新建一个app.ts
文件,这是express
项目的入口,然后使用compression
中间件和static
中间件,并监听端口,具体代码如下:
// 路径: package/server/app.tsimport express from "express";
import compression from "compression";const staticFilePath = ""; // 静态资源路径,先空着,一会在“其他工作”中再填充const main = async () => {const app = express();const port = 8081;app.use(compression()); // 使用compression中间件gzip静态资源文件app.use("/static", express.static(config.staticFilePath)); // 静态资源文件在服务器中的位置// 监听端口,起服务app.listen(port, () => {console.log(`server started at http://localhost:${port}`);});
};main();
4-2-2、接口开发
我们以开发“获取用户列表”接口为例。
首先,我们在package/server
目录下新建一个db.ts
文件,先直接把数据存到一个数组里吧,这样做简单一点,实际上我们会把这里替换为读数据库的逻辑。
// 路径: package/server/db.tsexport enum IUserStatus {INUSE,UNUSE,
}const user = [{ username: "user1", email: "user1@email.com", status: IUserStatus.INUSE },{ username: "user2", email: "user2@email.com", status: IUserStatus.INUSE },{ username: "user3", email: "user3@email.com", status: IUserStatus.INUSE },{ username: "user4", email: "user4@email.com", status: IUserStatus.INUSE },{ username: "user5", email: "user5@email.com", status: IUserStatus.INUSE },{ username: "user6", email: "user6@email.com", status: IUserStatus.UNUSE },
];export const getUser = () => {return user;
};
然后,我们在package/server
目录下新建一个service
目录,并在该目录下新建一个userService.ts
文件,再写一个UserService
类用来控制用户相关的逻辑,具体内容如下:
// 路径: package/server/service/userService.ts
import { getUser, IUserStatus } from "../db";// user相关的service
export class UserService {private userData = getUser();// 获取用户列表信息getUserData = () => {const output = this.userData.filter((item) => item.status === IUserStatus.INUSE);return output ? output : [];};
}
再然后,我们在package/server
目录下新建一个controller
目录,并在该目录下新建一个userController.ts
文件,再写一个UserController
类用来控制接口相关的逻辑,具体内容如下:
// 路径: package/server/controller/userController.tsimport { Request, Response } from "express";
import { UserService } from "../service/userService";export class UserController {private userService = new UserService(); // 实例化service// 获取用户列表的接口处理逻辑getUser = (req: Request, res: Response) => {try {const data = this.userService.getUserData();return res.status(200).json({ data, message: "get user successful" });} catch (e) {return res.status(500).json({ data: {}, message: e.message });}};
}
当我们完成接口处理逻辑后,就可以配置路由文件了,我们在package/server
目录下新建一个router.ts
文件,并配置路由以及命中路由时执行的方法,具体代码如下:
// 路径: package/server/router.tsimport express from "express";
import { UserController } from "./controller/userController";export const getRouter = () => {const userController = new UserController(); // 实例化controllerconst router = express.Router();router.get("/user", userController.getUser); // 配置路由执行的方法,当访问/user路径时,执行getUser方法return router;
};
最后,我们在package/server/app.ts
中注册路由,就完成获取用户列表接口的开发了,具体代码如下:
// 路径: package/server/app.tsimport express from "express";
import compression from "compression";
import { getRouter } from "./router";const staticFilePath = ""; // 静态资源路径,先空着,一会在“其他工作”中再填充const main = async () => {const app = express();const port = 8081;app.use(compression()); // 使用compression中间件gzip静态资源文件app.use("/static", express.static(config.staticFilePath)); // 静态资源文件在服务器中的位置app.use("/api", getRouter()); // 挂载路由// 监听端口,起服务app.listen(port, () => {console.log(`server started at http://localhost:${port}`);});
};main();
这样我们就完成了后端服务的开发了!
4-3、编写 npm script
后端代码编写完成后,我们还需要做一些工作才能让这个后端应用真的跑起来。
比如,开发时我们需要热更新,线上环境也需要进程守护,这些得单独写npm script
。
4-3-1、dev:server 命令
在开发时,我们需要使用ts-node
来直接执行typescript
文件,然后使用nodemon
来检测server
目录下的改动,并热更新。
那么我们就先执行pnpm add -g ts-node nodemon
来全局安装它们,然后在package.json
中写下如下命令:
// 路径: package.json"scripts": {// ... 其他命令"dev:server": "NODE_ENV=dev nodemon --watch './package/server/**/*.ts' --exec 'ts-node' ./package/server/app.ts",
},
这个命令会用nodemon
检测所有命中./package/server/**/*.ts
规则的文件的改动,如果有修改,就执行ts-node ./package/server/app.ts
命令,也就是执行app.ts
中的逻辑。
这样,我们执行pnpm run dev:server
就可以在localhost:8081
上启动服务,并开始愉快的开发了。
4-3-2、build:server 命令
而当我们开发完成后,我们需要使用typescript
提供的tsc
命令,来把typescript
文件编译成javascript
文件,也就是如下命令:
// 路径: package.json"scripts": {// ... 其他命令"build:server": "NODE_ENV=prod tsc --p ./package/server",
},
其中的--p ./package/server
是会选择 ./package/server
目录下的tsconfig.json
进行编译,这样就会把编译好的文件放到tsconfig.json
中指定的output
目录下
这样,我们执行pnpm run build:server
就可以编译后端代码了。
4-4、设置配置文件
另外,我们服务的开发环境和线上环境有一些不同的配置,我们还得写一段“根据环境读取配置”的逻辑。
还记得刚才空着的staticFilePath
变量吗?在我们前端的打包脚本中,打包好的文件会放到output/client
目录下,这就导致了在开发环境下和生产环境下staticFilePath
指向的目录不一致,我们就得在配置文件中单独配置他们。
在上面编写的npm script
中,我们开发环境设置了dev
的环境变量,线上环境设置了prod
的环境变量,我们可以根据这两个变量来区分不同的环境,并读取不同的配置,我们在package/server
目录下新建一个config
目录,并写入如下三个代码文件:
// 路径: package/server/config/dev.tsimport path from "path";export const developmentConfig = {staticFilePath: path.join(__dirname, "../../../output/client"),
};
// 路径: package/server/config/prod.tsimport path from "path";export const productionConfig = {staticFilePath: path.join(__dirname, "../client"),
};
// 路径: package/server/config/index.tsimport { developmentConfig } from "./dev";
import { productionConfig } from "./prod";const env = process.env.NODE_ENV;const getConfig = () => {if (env === "dev") {return developmentConfig;}return productionConfig;
};export const config = getConfig();
然后,我们使用getConfig
函数拿到对应的配置,并赋值给staticFilePath
即可,就像下述代码中注释写的这样:
// 路径: package/server/app.tsimport express from "express";
import { getRouter } from "./router";
import { config } from "./config";
import compression from "compression";const main = async () => {const app = express();const port = 8081;app.use(compression());app.use("/static", express.static(config.staticFilePath)); // 这里直接使用了配置文件中对应的配置app.use("/api", getRouter());app.listen(port, () => {console.log(`server started at http://localhost:${port}`);});
};main();
4-5、后端结构
这样后端就大功告成了!现在整体目录结构如下:
.
├── output # 编译产物
├── package
│ ├── client # 前端代码
│ └── server # 后端代码
│ ├── app.ts
│ ├── config
│ │ ├── dev.ts
│ │ ├── index.ts
│ │ └── prod.ts
│ ├── controller
│ │ └── userController.ts
│ ├── db.ts
│ ├── router.ts
│ ├── service
│ │ └── userService.ts
│ └── tsconfig.json
├── package.json
└── tsconfig.json
五、前端部分
本部分会介绍如何一步一步地使用vue3
和webpack
开发一个可以从后端接口拉取数据并展示的前端页面。
5-1、开发准备
前端首先执行pnpm add vue@next @vue/compiler-sfc @vue/runtime-dom -D
命令安装依赖,把它们安装到devDependencies
是因为我们实际在部署的时候,用的是打包好的资源文件,而不是直接依赖它们。
安装好依赖后,我们需要调整tsconfig.json
,我们可以在package/client
目录下建一个目录级的tsconfig.json
,用于控制client
目录下的typescript
文件,具体内容如下:
// 路径: package/client/tsconfig.json
{"extends": "../../tsconfig.json", // 继承根目录的tsconfig"compilerOptions": {"outDir": "../../output/client", // 编译后的js文件地址"moduleResolution": "node", // 模块解析策略"esModuleInterop": true, // 支持按照es6模块规范导入commonjs模块"target": "esnext", // 编译生成esnext规范的js代码"module": "esnext" // 编译生成的代码使用什么模块化规范},"include": ["./**/*.ts", "./**/*.d.ts"],"exclude": ["../../node_modules"]
}
然后,我们在package/client
目录下新建一个index.html
文件,这是我们的html
模板文件,我们会根据这个html
文件的结构,插入打包后的js
,css
资源文件,并输出最终的html
文件,具体代码如下:
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><title><%=htmlWebpackPlugin.options.title %></title></head><body><div id="root"></div></body>
</html>
同时,因为我们采用typescript
进行开发,我们需要手动声明vue
文件的类型,因此,我们需要新建一个package/client/type
目录,并在其中新建一个vue.d.ts
的声明文件,内容如下:
declare module "*.vue" {import type { DefineComponent } from "vue";const component: DefineComponent<{}, {}, any>;export default component;
}
另外,还要在package/client
目录下新建一个main.ts
文件和一个app.vue
文件,前者是我们webpack
打包的入口,后者是我们要挂载到DOM
上的根组件,具体内容可以后面再填充。
5-2、配置 webpack
我们选择webpack
来打包前端项目中的资源文件,还使用webpack-dev-server
来作为开发时的热更新服务器。因此,我们也需要配置一下webpack
用于满足这些需求。
首先我们使用pnpm add webpack webpack-cli webpack-dev-server webpack-merge -D
命令来安装webpack
相关的依赖。
然后我们还要继续添加一些webpack
的loader
和plugin
,这样我们才能正常打包项目,具体添加依赖的命令是:pnpm add vue-loader@next css-loader html-webpack-plugin mini-css-extract-plugin postcss-loader ts-loader -D
安装好依赖后,在package/client
目录下新建一个build
目录,并在这个目录中新建三个名为webpack.base.js
(基础配置)、webpack.dev.js
(开发配置)、webpack.prod.js
(线上配置)的文件,具体内容如下(配置的解析可以看注释):
// 路径: package/client/webpack.base.jsconst path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { VueLoaderPlugin } = require("vue-loader");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");module.exports = {entry: path.resolve(__dirname, "../main.ts"), // 打包文件入口module: {rules: [// 解析css文件{test: /\.css$/,use: [{loader: MiniCssExtractPlugin.loader,},"css-loader","postcss-loader",],},// 解析ts文件{test: /\.ts$/,loader: "ts-loader",options: {appendTsSuffixTo: [/\.vue$/],configFile: "./tsconfig.json",},exclude: /node_modules/,},// 解析vue文件{test: /\.vue$/,use: "vue-loader",},],},resolve: {extensions: [".ts", ".js", ".vue", ".json"], // 要打包的文件后缀alias: {vue: "@vue/runtime-dom", // 模块名简称},},plugins: [// 将打包好的js文件插入html中new HtmlWebpackPlugin({template: path.resolve(__dirname, "../index.html"),filename: "index.html",title: "template-project",}),// 打包vue模板文件需要实例化一个VueLoaderPluginnew VueLoaderPlugin(),],
};
// 路径: package/client/webpack.dev.jsconst { merge } = require("webpack-merge");
const base = require("./webpack.base.js");
const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");// merge函数会将webpack基础配置和dev配置合到一起
module.exports = merge(base, {mode: "development", // 开发模式devtool: "inline-source-map",output: {filename: "js/[name].[fullhash].js", // 输出js文件名称path: path.resolve(__dirname, "../../../output/client"), // 输出js文件路径},devServer: {port: 8080, // devServer的端口compress: true, // 是否压缩proxy: { context: ["/api", "/api"], target: "http://localhost:8081" }, // devServer的代理,会将请求代理到localhost:8081,也就是后端服务器上},plugins: [// 单独打包css文件new MiniCssExtractPlugin({filename: "css/[name].css",chunkFilename: "css/[id].css",}),],
});
// 路径: package/client/webpack.prod.jsconst { merge } = require("webpack-merge");
const base = require("./webpack.base.js");
const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");// merge函数会将webpack基础配置和prod配置合到一起
module.exports = merge(base, {mode: "production", // 开发模式output: {filename: "js/[name].[contenthash].js", // 输出js文件名称path: path.resolve(__dirname, "../../../output/client"), // 输出js文件路径},plugins: [// 单独打包css文件new MiniCssExtractPlugin({filename: "css/[name].[contenthash].css",chunkFilename: "css/[id].[contenthash].css",}),],
});
5-3、开发前端服务
接下来就是开发前端服务了,还记得刚才我们新建的main.ts
文件嘛?我们在这个文件中写入如下逻辑,用于将根组件挂载到DOM
中:
// 路径: package/client/main.tsimport { createApp } from "vue";
import App from "./page/app.vue";const app = createApp(App);
app.mount("#root");
然后,我们就可以在app.vue
中编写组件逻辑了,这个组件的作用是从后台拉取用户列表并展示。
在组件中,我们使用了axios
来发起http
请求,所以要先执行pnpm add axios -D
来安装相关依赖。
// 路径: package/client/app.vue<template><div class="user"><div v-for="(item, index) in userList" :key="`user-${index}`">用户名: {{ item.username }} 邮箱: {{ item.email }}</div></div>
</template><script lang="ts">
import { onMounted, reactive, toRefs } from 'vue';
import axios from 'axios';// 开发环境和线上环境的url不一样,通过环境变量进行区分
const DEV_BASE_URL = 'http://localhost:8081';
const PROD_BASE_URL = '';
const AXIOS_BASE_URL = process.env.NODE_ENV === 'dev' ? DEV_BASE_URL : PROD_BASE_URL;export default {setup() {const data = reactive({userList: []});onMounted(async () => {const res = await http.get(`${AXIOS_BASE_URL}/api/user`); // 使用axios发起请求data.userList = res.data.data; // 把数据赋值给userList});return {...toRefs(data) // 将userList变成reactive的,这样在模板中也可以访问了};}
};
</script><style scoped>
.user {margin-top: 50px;text-align: center;
}
</style>
5-4、编写 npm script
前端代码编写完成后,我们同样需要做一些工作才能让这个前端应用真的跑起来。
5-4-1、dev:client 命令
在开发前端页面时,我们需要起一个webpack-dev-server
来热更新,所以我们用webpack serve
命令启动dev-server
,并设置配置文件的路径为./package/client/build/webpack.dev.js
。
// 路径: package.json"scripts": {// ... 其他命令"dev:client": "NODE_ENV=dev webpack serve --progress --hot --config ./package/client/build/webpack.dev.js",
},
5-4-2、dev:all 命令
那么我们如果想前端后端一起开发,就直接把dev:server
和dev:client
放到一起执行就好了
// 路径: package.json"scripts": {// ... 其他命令"dev:all": "npm run dev:client & npm run dev:server",
},
5-4-1、build:client 命令
在打包的时候,我们使用webpack
命令进行文件打包,并设置配置文件的路径为./package/client/build/webpack.prod.js
就可以。
// 路径: package.json"scripts": {// ... 其他命令"build:client": "NODE_ENV=prod webpack --config ./package/client/build/webpack.prod.js"
},
5-4-2、build:all 命令
同样的,如果我们想前端后端一起打包,那么就直接把build:server
和build:client
放到一起执行就好了
// 路径: package.json"scripts": {// ... 其他命令"build:all": "npm run clean && npm run build:client && npm run build:server",
},
5-5、前端结构
这样我们前端、后端的代码就都编写完毕了!现在整体目录结构如下:
.
├── README.md
├── output # 打包产物
├── package
│ ├── client # 前端代码
│ │ ├── build # 打包配置
│ │ │ ├── webpack.base.js
│ │ │ ├── webpack.dev.js
│ │ │ └── webpack.prod.js
│ │ ├── index.html # html模板
│ │ ├── main.ts # 入口文件
│ │ ├── app.vue # 根组件
│ │ ├── tsconfig.json
│ │ └── type
│ │ ├── asset.d.ts
│ │ └── vue.d.ts
│ └── server # 后端代码
│ ├── app.ts
│ ├── config
│ │ ├── dev.ts
│ │ ├── index.ts
│ │ └── prod.ts
│ ├── controller
│ │ └── userController.ts
│ ├── db.ts
│ ├── router.ts
│ ├── service
│ │ └── userService.ts
│ └── tsconfig.json
├── package.json
└── tsconfig.json
5-6、其它
另外,在实际的模板项目(https://github.com/shadowings-zy/vue-express-ts-template-project)中,还有vuex
、vue-router
、加载svg
图片的逻辑,可以直接看代码。
六、部署部分
当我们想要在服务器上使用nodejs
运行编译好的javascrpit
文件,我们需要使用pm2
来守护这个node
进程,pm2
提供了很多功能,比如是在后台运行这个进程,在进程挂掉的时候及时重启等等。
那么我们就先执行pnpm add -g pm2
来全局安装它,然后在 package.json 中写下如下命令:
// 路径: package.json"scripts": {// ... 其他命令"start": "NODE_ENV=prod pm2 start output/app.js"
},
这样,我们先执行pnpm run build:all
把前后端编译产物输出到output
目录下,再执行pnpm run start
就可以在服务器上启动我们编写的node
服务了。
最后,浏览器输入localhost:8081/static/index.html
,就能访问我们的服务和页面了!
万字好文,手把手教你做一个「vue3+express+typescript」的全栈模板项目相关推荐
- 手把手教你实现一个「以图搜图」
朋友们,如需转载请标明出处:http://blog.csdn.net/jiangjunshow 准备工作 老样子,先来准备好我们此次需要使用到的工具: IDE:Pycharm Python:3.7 P ...
- python手机版做小游戏代码大全-Python大牛手把手教你做一个小游戏,萌新福利!...
原标题:Python大牛手把手教你做一个小游戏,萌新福利! 引言 最近python语言大火,除了在科学计算领域python有用武之地之外,在游戏.后台等方面,python也大放异彩,本篇博文将按照正规 ...
- 手把手教你做一个自己的chrome扩展程序
手把手教你做一个自己的chrome扩展程序 [目录] first.效果 1.收藏夹修改 (1).鼠标移动到收藏夹上的动作效果 (2).收藏夹框 (3)百度搜索框功能 2.右上文字修改 3.背景图片修改 ...
- 手把手教你做一个Java贪吃蛇小游戏
大家好,我是孙不坚1208,这篇博客给大家分享一下:如何做一个贪吃蛇小游戏(Java版)的exe应用程序,希望能给需要帮助的朋友带来方便. 手把手教你做一个Java贪吃蛇小游戏的exe应用程序 一.J ...
- Blender图解教程:手把手教你做一个马里奥金币 之 图片转法线贴图法(附模型下载)
<Blender图解教程:手把手教你做一个马里奥金币 之 比较传统的方法>介绍了一种用Blender制作法线贴图的流程,本文介绍一种更加省事的方法. 步骤 效果图 概要 步骤 1. 建模 ...
- 手把手教你做一个物联网视频监控项目(三)流媒体方案实现
往期文章 手把手教你做一个物联网视频监控项目(一) 介绍 手把手教你做一个物联网视频监控项目(二)MJPG-streamer方案实现 文章目录 前言 一.软硬件准备 二.流媒体方案的实现之FFmpeg ...
- 手把手教你做一个jsp servlet mysql实现的学生签到考勤请假管理系统附带视频开发教程和完整源码
今天给大家演示的是一款由jsp+servlet+my色口数据库实现的学生请假签到考勤管理系统,采用了MVC的设计模式,结构层次非常清晰,此外系统还有完整的开发教程. 下面我们先来看看文档结构: 下面来 ...
- 手把手教你做一个非常酷的PoV显示器(附源码)
关注+星标公众号,不错过精彩内容 来源 | DF创客社区 作者 | Amal Shajan 今天为大家分享一个DIY产品,如下: 前两天天我在浏览购物网站的时候,被一个购物清单吸引住了, 5个ATti ...
- R数据分析:跟随top期刊手把手教你做一个临床预测模型
临床预测模型也是大家比较感兴趣的,今天就带着大家看一篇临床预测模型的文章,并且用一个例子给大家过一遍做法. 这篇文章来自护理领域顶级期刊的文章,文章名在下面 Ballesta-Castillejos ...
- 手把手教你做一个新浪博客发布软件JAVA版本(5)--打开博客发布页面并解析博客内容
前言:很多人用新浪博客引流,但是以前可以用api发布,但是现在已经行不通了,市面上也有诸如新浪博客批量发布软件啦,新浪博客批量发帖啦,新浪博客发布软件啊等等的各种工具,但是小心中枪,一 ...
最新文章
- 【imx6】libipu.so.0说明
- 十分钟完成Bash 脚本进阶!列举Bash经典用法及其案例
- 图深度学习(GraphDL),下一个人工智能算法热点
- Redis Info详解
- 基于有限元方法的薄板冲压成型过程的程序仿真
- 求助:现在有一个可以进体制“养老”的坑,我该不该跳?
- 知乎超高赞:见识多的人,平时都在看些什么?
- 滴滴司机毒打投资人;华为回应自研系统;微信回应被删聊天记录可提取 | 一周业界事...
- USB及手机平板设备插拔响应解决方案
- VUE3.0引入本地js文件
- AutoCAD如何自动加载dll文件
- win10关闭某个端口
- 全球及中国焦磷酸钠行业需求态势及发展趋势预测报告(2022-2027年)
- word设置标题自动编号
- JavaScript学习笔记(四) ES6
- 自制IDEAWebStorm主题,轻仿VsCode-One Dark Pro
- 关于 ideaIU 20191.2的基本安装教程及破解
- PostgreSQL+PostGIS的使用
- 怎么把Android手机号码导入,手机联系人怎么导入?通讯录vcf导入安卓手机的方法...
- 面试官常问的 web前端 问题(二)