从0开始搭建项目,相信更多人都做过,但是你真的独立搭建整个项目了吗,有没有考虑周全,是否从UI的框架的选型、权限、图标、路由、登陆拦截、第三方工具库、性能优化等多方面搭建出适合本公司项目的人员和环境。本文总结的是Vue-Cli3.0构建项目
搭建项目
根据实际情况而定。 值得注意的Vue-Cli 初始化项目的命令是vue create 项目名称
,而不是之前的Vue init webpack 项目名称
我这边大致选了history路由、Vuex、less、eslint、unit test等
[图片上传失败...(image-dc9626-1556976269888)]
Vue-Cli官方地址
webpack、babel、vue.config.js的配置
vue.config.js的相关配置
// vue.config.js
const path = require('path')
const resolve = dir => {
return path.join(__dirname, dir)
}
// 线上打包路径,请根据项目实际线上情况
const BASE_URL = process.env.NODE_ENV === 'production' ? '/' : '/'
module.exports = {
publicPath: BASE_URL,
outputDir: 'dist', // 打包生成的生产环境构建文件的目录
assetsDir: '', // 放置生成的静态资源路径,默认在outputDir
indexPath: 'index.html', // 指定生成的 index.html 输入路径,默认outputDir
pages: undefined, // 构建多页
productionSourceMap: false, // 开启 生产环境的 source map?
configureWebpack: { // webpack 的配置
plugins: [
new MyAwesomeWebpackPlugin()
]
},
chainWebpack: config => { // webpack的配置(链式操作)
// 配置路径别名
// config.resolve.alias
// .set('@', resolve('src'))
// .set('_c', resolve('src/components'))
},
css: {
modules: false, // 启用 CSS modules
extract: true, // 是否使用css分离插件
sourceMap: false, // 开启 CSS source maps?
loaderOptions: {
less: {
modifyVars: { // 定制主题
'primary-color': '#1DA57A',
'link-color': '#1DA57A',
'border-radius-base': '2px',
},
javascriptEnabled: true,
}
}
},
devServer: {
port: 8080, // 端口
proxy: '' // 设置代理
}
}
Vue.config.js官方地址
babel.config.js
使用babel-plugin-import
(按需加载组件代码和央视的babel插件),下面的配置使用该插件引入 vue-antd-design
// babel.config.js
module.exports = {
presets: ["@vue/app"],
plugins: [
[
"import",
{ libraryName: "ant-design-vue", libraryDirectory: "es", style: true }
]
]
};
UI框架(Ant-design-vue)
全局引入
不多说。想说的是全局引入有13MB,50多个组件,全部引入有点大。其实在我以前做过的项目当中基本上都是全局引入,不知大佬们写代码会不会有“洁癖”,局部引入或是自己写,哈哈
[图片上传失败...(image-2208b0-1556976269888)]
局部引入
// main.js
import { Button } from "ant-design-vue"
import "ant-design-vue/dist/antd.less"
// 或者是
import "ant-design-vue/lib/button.less"
Vue.use(Button)
vue-and-design官方地址
配置就在上方的babel.config.js
中。
我们下面看看main.js
是怎么使用的
// main.js
import { Button,Menu,Drawer,Radio,Layout,Icon} from 'ant-design-vue';
Vue.use(Drawer)
Vue.use(Button)
Vue.use(Menu)
Vue.use(Radio)
Vue.use(Layout)
Vue.use(Icon)
哎呀,貌似篇幅很长,不过加载的文件大小确实小了很多
路由配置
一定要配置路由,就得想好文件目录怎么设置。这里参考了唐老师的搭建项目的目录
[图片上传失败...(image-f8195c-1556976269888)]
路由配置,篇幅有限。我只选出一部分
// router.js
import Vue from "vue";
import Router from "vue-router";
import { notification } from "ant-design-vue";
import NotFound from "./views/404";
import Forbidden from "./views/403";
Vue.use(Router);
const router = new Router({
mode: "history",
base: process.env.BASE_URL,
routes: [
{
path: "/user",
hideInMenu: true,
component: () =>
import( "./layouts/UserLayout"),
children: [
{
path: "/user",
redirect: "/user/login"
},
{
path: "/user/login",
name: "login",
component: () =>
import( "./views/User/Login")
}
]
},
{
path: "/",
meta: { authority: ["user", "admin"] }, // 用来做权限管理
component: () =>
import( "./layouts/BasicLayout"),
children: [
// dashboard
{
path: "/",
redirect: "/dashboard/analysis"
},
{
path: "/dashboard",
name: "dashboard",
meta: { icon: "dashboard", title: "仪表盘" },
component: { render: h => h("router-view") },
children: [
{
path: "/dashboard/analysis",
name: "analysis",
meta: { title: "分析页" },
component: () =>
import( "./views/Dashboard/Analysis")
}
]
},
// form
{
path: "/form",
name: "form",
component: { render: h => h("router-view") },
meta: { icon: "form", title: "表单", authority: ["admin"] },
children: [
{
path: "/form/basic-form",
name: "basicform",
meta: { title: "基础表单" },
component: () =>
import("./views/Forms/BasicForm")
},
{
path: "/form/step-form",
name: "stepform",
hideChildrenInMenu: true,
meta: { title: "分布表单" },
component: () =>
import( "./views/Forms/StepForm"),
children: [
{
path: "/form/step-form",
redirect: "/form/step-form/info"
},
{
path: "/form/step-form/info",
name: "info",
component: () =>
import( "./views/Forms/StepForm/Step1")
}
]
}
]
}
]
},
{
path: "/403",
name: "403",
hideInMenu: true,
component: Forbidden
},
{
path: "*",
name: "404",
hideInMenu: true,
component: NotFound
}
]
});
export default router;
总结下分为三部分:用户模块、主要内容、403/404页面。 用户下面有子模块login,主要内容(一般是导航菜单的内容)有仪表盘dashboard
和表单form
,仪表盘下面有分析页anlysis
,表单下面有基础表单basicForm
// App.vue
BasicForm.vue
[图片上传失败...(image-e9b0ec-1556976269888)]
路由大概到这里
这个写法觉得很巧妙
[图片上传失败...(image-77953-1556976269888)]
路由守卫
如果没有权限跳到登陆页面,
登陆成功之后没有权限进入403页面,更多路由守卫知识点击
router.beforeEach((to, from, next) => {
// to 即将要进入的目标 路由对象
// from 当前导航正要离开的路由
// next
if (to.path !== from.path) {
NProgress.start();
}
const record = findLast(to.matched, record => record.meta.authority);
if (record && !check(record.meta.authority)) {
if (!isLogin() && to.path !== "/user/login") {
next({
path: "/user/login"
});
} else if (to.path !== "/403") {
notification.error({
message: "403",
description: "你没有权限访问,请联系管理员咨询。"
});
next({
path: "/403"
});
}
NProgress.done();
}
next();
});
// 全局后置钩子
router.afterEach(() => {
NProgress.done();
});
权限管理
菜单路由
在路由配置那里有个元数据信息,那里设置了哪些用户具有菜单权限。 在真实的环境中是后端返回这个用户的菜单,有的话就渲染出来
// 检查 用户的权限
export function check(authority){
const current = getCurrentAuthority();
return current.some(item => authority.includes(item));
}
这里是自定义指令的方式做的,更多自定义指令的参数点击
[图片上传失败...(image-1f9a6f-1556976269888)]
import { check } from "../utils/auth";
function install(Vue, options = {}) {
Vue.directive(options.name || "auth", {
inserted(el, binding) {
if (!check(binding.value)) {
el.parentNode && el.parentNode.removeChild(el);
}
}
});
}
export default { install };
然后在main.js
中使用
// main.js
import Auth from './directives/auth';
Vue.use(Auth);
其它相关
Nprogress 过渡组件
yarn add nprogress
npm install --save-dev nprogress
[图片上传失败...(image-d158e3-1556976269888)]
[图片上传失败...(image-a44ce3-1556976269888)]
更多点击
之前创建的锚点标题组件是比较简单,没有管理任何状态,也没有监听任何传递给它的状态,也没有生命周期方法。实际上,它只是一个接受一些 prop 的函数。
在这样的场景下,我们可以将组件标记为 functional,这意味它无状态 (没有响应式数据),也没有实例 (没有 this 上下文)。
render()函数还是一个挺有意思的存在
loash库
resize-detector
不知道在哪里看过一句话,码而不思则殆,思而不码还是殆