Node.js 开发速度快、性能优秀,是现在快速开发小型后台系统的首选。下面将介绍使用Node.js作为服务器端,D2Admin和Vue开源框架开发后台管理系统。
完成界面预览:
运行环境
在 Node.js 官网 下载合适您系统的 Node.js。
您最少需要安装 Node.js 8+,当前版本为14
下载项目
前往仓库下载源码:
-
完整版仓库 Github | 码云(国内)
-
简化版起始模板 Github | 码云(国内)
挑选一个您喜欢的网站 Fork 代码到您的仓库,然后下载或克隆。
我的建议是使用 简化版起始模板 因为在完整版的基础上做减法,要比在简化版基础上做加法复杂。
下面的步骤都以简化版起始模板为基础演示如何使用。
下载完成后您会得到这些资源(以 1.1.5 版本为例):
安装依赖
将终端切换到项目目录之后安装依赖:
npm i
开发调试
在项目目录内启动项目:
npm run dev
或者使用:
npm start
也可以使用 cli3 新推荐的命令:
npm run serve
以上每种方式效果都是一样的,只是为了照顾不同人的习惯起了三个名字,实质上都是运行了 vue-cli-service serve --open
。
成功运行后会显示后台登录界面:
新建页面
src/pages
目录是页面存放目录,在此目录下新建目录,例如 page-demo:
然后在文件夹内新建 index.vue 文件:
<template>
<d2-container>
<template slot="header">header</template>
Hello World
<template slot="footer">footer</template>
</d2-container>
</template>
上面的代码生成了一个带有 header 和 footer 的页面,页面内容只有一句话。
设置路由
src/router/routes.js
是路由配置文件。
在 frameIn
变量最后添加:
{
path: '/page-demo',
name: 'page-demo',
component: () => import('@/pages/page-demo'),
meta: {
auth: true,
title: '新建示例'
}
}
最后 frameIn
变量应该是这样:
const frameIn = [
{
path: '/',
redirect: { name: 'index' },
component: layoutHeaderAside,
children: [
{
path: 'index',
name: 'index',
meta,
component: () => import('@/pages/index')
},
// ...
{
path: '/page3',
name: 'page3',
component: () => import('@/pages/page3'),
meta: {
auth: true,
title: '页面 3'
}
},
{
path: '/page-demo',
name: 'page-demo',
component: () => import('@/pages/page-demo'),
meta: {
auth: true,
title: '新建示例'
}
}
]
}
]
设置菜单
src/menu
目录是菜单存放目录,里面的两个文件分别存放顶栏和侧边栏的菜单。
现在将顶栏和侧边栏菜单分别存放然后赋值的逻辑只是一个最简单的演示,请根据您的需要自己修改菜变化逻辑,您可以只用一行代码就可以动态更新菜单。
打开 src/menu/header.js
添加新的菜单(高亮行):
export default [
{ path: '/index', title: '首页', icon: 'home' },
{
title: '页面',
icon: 'folder-o',
children: [
{ path: '/page1', title: '页面 1' },
{ path: '/page2', title: '页面 2' },
{ path: '/page3', title: '页面 3' },
{ path: '/page-demo', title: '新建示例' }
]
}
]
打开 src/menu/aside.js
添加新的菜单(高亮行):
export default [
{ path: '/index', title: '首页', icon: 'home' },
{
title: '页面',
icon: 'folder-o',
children: [
{ path: '/page1', title: '页面 1' },
{ path: '/page2', title: '页面 2' },
{ path: '/page3', title: '页面 3' },
{ path: '/page-demo', title: '新建示例' }
]
}
]
后台效果
经过上述步骤您得到了一个具有顶栏和底栏的页面,这个页面的入口在顶栏菜单和侧边栏菜单都会显示,并且打开此页面时浏览器 title 也会做出相应更新。
路由
数据
完整版的 D2Admin 路由设置文件在 d2-admin/src/router/routes.js
。
需要注意的是大多数路由设置要写在 frameIn 下。frameIn 中的页面会在嵌套在一个包含顶栏和侧边栏的页面布局中显示。
路由配置
路由注册和钩子设置文件为 d2-admin/src/router/index.js
,此文件中将 routes.js
中导出的 routes 变量注册到路由,并且设置后续的路由拦截和登录状态判断,多页面的新建页面事件触发也来自这里。该文件再导出注册了所有页面的路由实例,在 main.js
中注册到 vue 根实例。
路由缓存
开启缓存的页面可以在切换后保留页面数据。
路由缓存默认为关闭状态,如果想启用某个页面的缓存,请在该路由配置的 meta 中加入:
meta: {
cache: true
}
最后结果类似这样:
{
path: '/page-cache/on',
name: 'page-cache-on',
component: () => import('xxx.vue'),
meta: {
auth: true,
cache: true,
title: '开启缓存'
}
}
路由配置上开启缓存之后,还需要遵守一个约定才可以正确缓存页面:
页面组件必须有 name 字段,并且字段的值和该路由的 name 字段一致。请注意下面的高亮行。
例如:
- 页面文件:
<template>
<d2-container>
Hello World
</d2-container>
</template>
<script>
export default {
name: 'page-cache-on'
}
</script>
- 路由设置:
{
path: '/page-cache/on',
name: 'page-cache-on',
component: () => import('xxx.vue'),
meta: {
auth: true,
cache: true,
title: '开启缓存'
}
}
菜单
D2Admin 的顶栏菜单和侧边栏菜单相互独立互不影响。
数据源
顶栏数据使用 $store.state.d2admin.menu.header
作为数据源。
侧栏数据使用 $store.state.d2admin.menu.aside
作为数据源。
设置菜单
顶栏使用 $store.commit('d2admin/menu/headerSet', menu)
更新数据源。
侧栏使用 $store.commit('d2admin/menu/asideSet', menu)
更新数据源。
菜单联动
首先请明确一点,D2Admin 完整版的菜单联动只是一个简单方案,不要拘泥于此演示,下面介绍完整版联动的实现帮助您更快速实现自己的联动方案:
设置数据源
在 d2-admin/src/menu/index.js
中导出了两个变量,分别是:
- menuAside 预备侧边栏菜单
- menuHeader 顶栏菜单
在 main.js 中使用下面的方式导入
import { menuHeader, menuAside } from '@/menu'
menuHeader 在初始化根实例时在 created 生命周期直接赋值给顶栏菜单
this.$store.commit('d2admin/menu/headerSet', menuHeader)
然后在根实例中添加 $route.matched 的监听器
watch: {
'$route.matched' (val) {
const _side = menuAside.filter(menu => menu.path === val[0].path)
this.$store.commit('d2admin/menu/asideSet', _side.length > 0 ? _side[0].children : [])
}
}
这段代码的作用是当路由改变时,根据当前的路由找到 menuAside 中匹配当前路由对象中顶级路由的那个项目,并且赋值给侧边栏菜单达到联动效果。
您可以删除这部分代码,随意使用 vuex 中提供的顶栏和侧边栏菜单的设置方法来实现自己的菜单联动。
异步请求
D2Admin 使用 axios 作为异步请求工具,并做了一些封装。
axios | 地址 |
---|---|
Github | https://github.com/axios/axios |
npm | https://www.npmjs.com/package/axios |
中文文档 | https://www.kancloud.cn/yunye/axios/234845 |
介绍
Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。
- 支持浏览器和node.js
- 支持promise
- 能拦截请求和响应
- 能转换请求和响应数据
- 能取消请求
- 自动转换JSON数据
- 浏览器端支持防止CSRF(跨站请求伪造)
使用方式
axios 默认的使用方式在这里不做介绍,D2Admin 推荐在您的项目中使用下面的方式获取数据:
配置接口地址
默认的请求地址在 .env
VUE_APP_API=/api/
上述设置将在您访问 foo
时实际去访问 /api/foo
环境区分
如果您希望不同的环境使用不同的请求地址,可以在 .env.development
中添加设置(示例):
VUE_APP_API=/api-dev/
这样您在开发环境和正式环境就有了不同的公共请求地址,在开发环境访问 foo
时实际去访问 /api-dev/foo
详情参考
Vue CLI 3 | 环境变量和模式
示例
假设有一登录接口:
- 开发环境
http://127.0.0.1:8080/api/login
- 生产环境
http://127.0.0.1:3000/login
配置:
- .env.development
VUE_APP_API=http://127.0.0.1:8080/api/
- .env
VUE_APP_API=http://127.0.0.1:3000/
网络请求:
request({
url: 'login',
method: 'post',
data: {
username: '',
password: ''
}
})
即可实现在不同环境下访问不同的接口位置。
响应拦截
您在开始使用 D2Admin 开发您的项目之前,应该首先修改 d2-admin/src/api/service.js
下的响应拦截器设置。
默认约定
默认设置下 response.data 的数据格式应为如下所示:
{
// 接口约定的状态码 非 http 状态码
code: 0,
// 接口返回请求状态信息
msg: '返回信息',
// data 内才是真正的返回数据
data: {
list: [
...
]
}
}
在响应拦截器中会对 http 状态码以及 response.data.code
进行判断,如果全部为正常将会返回 response.data.data
,如果有错误将会触发日志记录和信息显示,如果是登录状态失效将自动清空本地的登录状态并退回到登录页面。所有的判断逻辑请您根据实际业务需要自行修改。
{
list: [
...
]
}
设计 API
自 v1.11.0 开始,D2Admin 开始使用一个同类项目中不多见的(在当时看来)方式配置接口,放弃使用 mock.js 模拟网络请求。
src/api 目录示例:
├─modules ---------- // 按功能模块划分成组的接口配置
│ ├─goods.api.js
│ ├─sys.role.api.js
│ └─sys.user.api.js
├─index.js --------- // 入口
├─service.js ------- // 请求实例设置
└─tools.js --------- // 相关工具函数
index.js
此文件作为网络请求配置的入口文件,作用是集中注册 modules 文件夹中的配置文件。
service.js
此文件为请求实例的配置,默认导出了两套请求实例(请根据实际业务需要合理添加)和一个 mock
方法:
- 用于真实网络请求的实例和请求方
service
request
- 用于模拟网络请求的实例和请求方法
serviceForMock
requestForMock
- 网络请求数据模拟工具
mock
这些方法都会被传入 modules 目录中的每一个接口配置中,使用方式详见下文介绍。
modules
modules 内任所有的 *.api.js 文件都会被调用,每个文件应该默认导出一个方法,这个方法接收一个对象作为参数,index.js 会将通用的方法通过此对象传递给每一个 module。
附注
- 较旧版本只扫描 modules 目录下第一级目录 <Badge text="1.19.0" type="warning"/>
- 在 modules 文件夹内扫描 *.api.js 而不是 *.js 的好处是这样允许您在 modules 文件夹内放置某些非接口配置的 js 文件,较旧的版本会识别所有的 js 文件 <Badge text="1.20.0" type="warning"/>
参数对象默认情况下的结构:
{
service,
request,
serviceForMock,
requestForMock,
mock,
faker,
tools
}
其中包含了上文提到的 service.js 导出的两组网络请求工具实例和对应的请求方法、模拟请求的配置函数,另外还有用于生成模拟数据的 faker
方法以及实用工具 tools
。借助这些方法,您可以在每一个 module 中设置多个网络请求配置。
配置示例:
export default ({ service, request, serviceForMock, requestForMock, mock, faker, tools }) => ({
/**
* @description 方法名称
* @param {Object} data 请求携带的信息
*/
DEMO_FETCH (data = {}) {
// 模拟数据
mock
.onAny('demo')
.reply(config => {
// 您可以这样在拦截请求时获取携带的数据
const data = tools.parse(config.data)
// 模拟正确的返回 并使用 faker 生成假数据
return tools.responseSuccess({ id: faker.random.uuid() })
// 模拟失败的返回
// return tools.responseError({}, '错误信息')
})
// 接口请求
// 如果这个接口不需要模拟了,请使用 request 代替 requestForMock
return requestForMock({
url: 'demo',
method: 'post',
data
})
}
})
使用:
在 vue 组件中
{
methods: {
async getTableData () {
try {
const res = await this.$api.DEMO_FETCH()
console.log(res)
} catch (error) {
console.log(error)
}
}
}
}
在任意 .js 文件中
import api from '@/api'
api.DEMO_FETCH()
您也可以参照 d2-projects/d2-admin-xiya-go-cms 项目,将
api
传入 store 中每个模块的生成方法中,省去在 store 模块中的 api import。
建议将一组接口写入一个配置文件,例如商品的增删改查:
export default ({ service, request, serviceForMock, requestForMock, mock, faker, tools }) => ({
GOODS_ADD (data) {...},
GOODS_REMOVE (id) {...},
GOODS_EDIT (data) {...},
GOODS_QUERY (query) {...},
})
更详细的文档参考
- faker https://github.com/Marak/faker.js
- axios-mock-adapter https://github.com/ctimmerm/axios-mock-adapter
跨域问题
如果您的前端项目和后端接口发生跨域,可以在本地配置代理:
devServer: {
proxy: {
'/api': {
target: 'http://47.100.186.132/your-path/api',
ws: true,
changeOrigin: true,
pathRewrite: {
'^/api': ''
}
}
}
}
上述配置的结果是在请求 /api/login
时转发到 http://47.100.186.132/your-path/api/login
。
详细的代理配置
Vue CLI 3 | devServer.proxy
常见问题
用户反馈问题,集中展示,方便后续用户自助解决问题
如何新增一个主题
新增主题所需的图片文件
public/image/theme/your-theme-name/logo/all.png
public/image/theme/your-theme-name/logo/icon-only.png
public/image/theme/your-theme-name/preview@2x.png
新增主题样式文件
src/assets/style/theme/your-theme-name/index.scss
src/assets/style/theme/your-theme-name/setting.scss
修改以下文件来注册新的主题
src/assets/style/theme/register.scss
src/setting.js
代码下载慢
使用国内的码云地址下载
无法启动项目
如果在 run dev
或者 npm i
的过程中报错,首先建议您升级 node 版本 > 8,在以下环境测试可用
➜ ~ npm -v
5.6.0
➜ ~ node -v
v8.11.1
➜ ~ nrm -V
1.0.2
➜ ~ nrm ls
npm ---- https://registry.npmjs.org/
cnpm --- http://r.cnpmjs.org/
* taobao - https://registry.npm.taobao.org/
nj ----- https://registry.nodejitsu.com/
rednpm - http://registry.mirror.cqupt.edu.cn/
npmMirror https://skimdb.npmjs.com/registry/
edunpm - http://registry.enpmjs.org/
推荐使用 nrm 管理 npm 源,不建议使用 cnpm
没有自动打开浏览器
如果您运行
npm run serve
npm run start
npm run dev
其中任何一个,编译完成后没有自动打开浏览器,请尝试在本机安装 vue-cli3 后重新启动项目。
热更新失效
如果使用了 cnpm,cnpm 使用 symlink,导致 webpack 打包的时候抒符号链接的包打包了两份,由于webpack/hot/emitter.js 为两个不同实例,导致 webpack hot/server webpackHotUpdate 无法触发,热更新失效。解决方法:设置 registry,不使用 cnpm。
npm config set registry https://registry.npm.taobao.org
npm install
如果上面的方法无效,尝试在 vue.config.js
中如下设置:
module.exports = {
chainWebpack: config => {
config.resolve
.symlinks(true)
return config
}
}
如果不清楚怎么写可以参考新版 D2Admin 的配置。
There is a resolve.symlinks in webpack configuration, and it's default value is true. Howerver, project created by vue-cli, the resolve.symlinks is set to false.
其它解决办法请参考 vue-cli/issues/1559。
node-sass 安装失败
由于某些不可描述的原因,利用 npm 进行安装模块的时候会发生包下载失败的情况,node-sass 尤其的频繁,或者说 node-sass 的二进制文件是接近百分百失败的,即使用 yarn 安装也依旧在这个点失败,给出以下建议
方法1
首先,需要提前下载 node-sass 的二进制文件,这个文件可以去 cnpm 仓库下载或者 node-sass 的 github 上去下载,在下载之前我们需要先查看电脑的系统的版本,来确定适合哪个版本的二进制文件,查看版本的指令如下:
node -p "[process.platform, process.arch, process.versions.modules].join('-')"
输入这个指令后会弹出一个系统版本,然后在下面两个地址中选择一个去下载对应系统版本的后缀为 .node 的 node-sass 文件
cnpm https://npm.taobao.org/mirrors/node-sass/
github https://github.com/sass/node-sass/releases
下载完保存到任意位置,最好放置到 package.json 所在位置。然后我们需要手动指定 node-sass 二进制文件的下载源为下载的那个文件,以下是npm与yarn的指令:
npm
npm config set sass-binary-path 你存放刚才下载的二进制文件的目录
// 例如 npm config set sass-binary-path e:/web/win32-x64-48_binding.node
yarn
yarn config set sass-binary-path 你存放刚才下载的二进制文件的目录
// 例如 yarn config set sass-binary-path e:/web/win32-x64-48_binding.node
然后我们即可用正常指令下载了
注意
此方法会绑定为本地文件,即无法更新 node-sass 了。如果不希望这样,请使用第二种方法
方法2
此方案将把下载源指定为cnpm仓库,更建议使用这种方法
全部的下载源指向cnpm的指令
npm
npm config set registry http://registry.npm.taobao.org
yarn
yarn config set registry http://registry.npm.taobao.org
只指定node-sass的下载源(建议使用)
npm
npm config set sass-binary-site http://npm.taobao.org/mirrors/node-sass
yarn
yarn config set sass-binary-site http://npm.taobao.org/mirrors/node-sass
关闭 ESLint 👎
由于使用者技术水平参差不齐,有些开发者希望在开发时关闭 ESLint,虽然我不建议这样做,在这里也给出以下方法,或者你也可以在下面的链接里找到答案
ESLint 中文 | ESLint 英文
- 方法 1
在根目录的 .eslintignore
中添加 *.vue
,就会忽略所有 vue 文件后缀的检查,js 文件同理。修改后重启本地服务。
- 方法 2
在根目录中的 .eslintrc.js
找到 '@vue/standard'
并注释掉,修改后重启本地服务。
修改 ESLint 规则
在根目录中的 .eslintrc.js
配置规则
语法
rules: {
"规则名": [规则值, 规则配置]
}
规则值
"off" 或者 0 //关闭规则关闭
"warn" 或者 1 //在打开的规则作为警告(不影响退出代码)
"error" 或者 2 //把规则作为一个错误(退出代码触发时为1)
- 规则列表 中文
- 规则列表 英文
侧边栏等部分无法点击
如果您打开了 Chrome 的开发者工具并且模拟了非 PC 设备(或者您真的是在 ipad 上访问的项目),在 1.6.11 版本之前,侧边栏和优化滚动模式的 d2-container
组件将会无法点击。请按照 0c8d38d 修改您的代码。
造成这个问题的原因是 better-scroll 插件会默认禁止 click 事件。
参见 《better-scroll 文档 | options.click》
无法跳转路由
有可能你在 D2Admin 的基础上进行你的开发时,发现在登录页面进行
this.$router.push({ name: 'index' })
跳转的时候页面并没有跳转,这是因为你很有可能没有进行下面的操作:
Cookies.set('token', res.token, setting)
原因根源是在 src/router/index.js
中有如下一段代码,根据 token
判断用户是否登录
router.beforeEach((to, from, next) => {
if (to.matched.some(r => r.meta.auth)) {
if (Cookies.get('token')) {
next()
} else {
next({
name: 'login'
})
}
} else {
next()
}
})
所以如果你没有存 token 字段在 cookie 中,路由鉴权机制将会重定向到登录页
如果你想修改基于 token 验证用户登录状态的机制,请在 ./src
下搜索 token
关键字并修改他们,但是我十分不建议你修改它们
最好的做法是在登录后返回本次登录的 token 并且存储在 cookie 中,然后在每次 ajax 请求时都携带这个 token 给后端做权限验证(必要的话你可以还可以增加 token 的更新机制)
同样需要注意的地方
除了需要在 cookie 中保存 token,你还要保存 uuid 字段,意为“用户唯一标识”Cookies.set('uuid', res.uuid, setting)
D2Admin 会在很多地方使用 cookie 中的此字段区分用户,比如不同用户选择的不同主题的数据持久化,还有不同用户打开的多标签页数据的持久化存储。
删除页面右上角 github 链接
在 src/components/demo/d2-demo-page-cover/index.vue
中删除相关代码即可
兼容性
首先 vue.js 和 ElementUI 做不到兼容的,D2Admin 肯定也兼容不了,实测在 macOS 下 Chrome 和新版本的火狐浏览器以及 Safari 都正常使用,这类管理系统一般是内部使用,通常不必太纠结兼容低版本浏览器,如果你发现了显示的 bug,可以加 QQ 群反馈,如果你可以修复这个 bug 使其在你的浏览器上显示正常,欢迎你的 pr。
项目里有未完成的代码
有些示例(比如 v-charts 和 ElementUI)是很方便就可以找到官网示例和文档的,这些插件和组件的示例在本项目中就可能处于未完成的状态,但是以后会完成
unexpected end of file
报错代码
E:\VS\TFS_FREE_Z\BaseProjects\VUE\d2-admin-z>npm i
npm WARN tar zlib error: unexpected end of file
npm ERR! cb() never called!
npm ERR! This is an error with npm itself. Please report this error at:
npm ERR! <https://github.com/npm/npm/issues>
npm ERR! A complete log of this run can be found in:
npm ERR! C:\Users\ZHZ\AppData\Roaming\npm-cache\_logs\2018-07-27T13_13_56_693Z-debug.log
解决方法
目前只有一位开发者遇到这个问题,最后使用 cnpm 绕过了这个错误
npm install -g cnpm --registry=https://registry.npm.taobao.org
打包后 CSS 文件位置出错
如果您发现打包后 CSS 文件出现在 dist 文件夹目录内,请检查 d2-admin/vue.config.js
中 baseUrl
的设置。
baseUrl
为项目部署的基础路径,应该以 '/' 开始并且以 '/' 结束
假设你的应用将会部署在域名的根部,比如 https://www.my-app.com/,那么 baseUrl
应该值为 '/'
如果你的应用时部署在一个子路径下,那么你需要在这里指定子路径。比如,如果你的应用部署在 https://www.foobar.com/my-app/,那么将这个值改为 /my-app/
打包后无法运行
打包后会生成 dist 文件夹,请在本地开启一个 http 服务来运行打包后的项目。
如果您不清楚如何操作,建议您使用 browsersync 快速达到目的。
Browsersync能让浏览器实时、快速响应您的文件更改(html、js、css、sass、less等)并自动刷新页面。更重要的是 Browsersync可以同时在PC、平板、手机等设备下进项调试。您可以想象一下:“假设您的桌子上有pc、ipad、iphone、android等设备,同时打开了您需要调试的页面,当您使用browsersync后,您的任何一次代码保存,以上的设备都会同时显示您的改动”。无论您是前端还是后端工程师,使用它将提高您30%的工作效率。
灰度模式下弹出框被遮住
如果您发现 dialog 在 D2Admin 灰度模式打开情况下被遮罩遮住,可以尝试在弹出框上添加 :append-to-body="true"
,将 Dialog 自身是插入至 body 元素上。
如何更新版本
首先明确告诉大家,此项目无法通过 npm 升级,亦无法通过 cli 升级。
D2Admin 不是一个独立的库,不能像其它插件一样通过 npm 升级版本,不仅这个项目,其实所有的 vue 管理系统脚手架都一样,由于本质上这类项目是一个初始模板,开发者需要做的是下载这些代码,并且根据自身需要去修改它们,然后基于此再去添加自己的业务代码,遂一旦您将代码下载到本地并且发生了修改,您是很难再跟随我们升级的。
iview-admin 作者在一次线下活动中也回答了现场观众的类似问题:
“这类项目一旦下载开始使用,基本是无法更新的。你在哪个时间点开始使用,这个项目就固定在什么版本了。”
那么可能您 follow 了我,或者 watch 了这个项目,看到我们经常更新代码,发布新的版本,也希望跟随一起更新,那怎么办呢?真的没有办法吗?
有以下两种可行方案:
- 请您在基于 D2Admin 开发时尽量避免太多的自带模块或者组件改动,这样您可以在发现新版本时下载新的脚手架,并且较方便地将您的业务代码逐步迁移过去。这个方案比较繁琐,所以不建议经常使用,一个月或者几个月一次即可。
- 我们会尽量在发版时标明每个改动的提交记录,这样您可以选择性地更新部分新代码到您的项目中。同样在开发时尽量避免太多的自带模块或者组件改动,这样您可以直接将新的某个文件替换到您的项目中。