简介
实现效果:常见的后台管理系统的 demo。地址:https://github.com/MaTonna/vue-cli-ts-ivew
新增了一个react版的。地址: https://github.com/MaTonna/react-ts-antd-router
项目启动
安装依赖
npm i
项目启动
npm run serve
http://127.0.0.1:3000/page1/index1/contentCenter2 是单页面的类似后台管理的页面,同时这个系统配置了多页面的
http://127.0.0.1:3000/index,是一个丑陋的首页
安装项
- 全局安装新包:npm install -g @vue/cli(要求 node 版本>=8.9,否则会报错要求升级 node,到官网重新下载即可)
- 创建项目:vue create hello-world
- 安装 less:npm install -D less-loader less
- 安装 ts:vue add @vue-typescript,可选用 vue-property-decorator
- 安装 babel:vue add @vue/cli-plugin-babel
vue.config.js 具体配置
const path = require('path');
const glob = require('glob');
const title = '测试页面';
const addStyleResource = rule => {
rule
.use('style-resource')
.loader('style-resources-loader')
.options({
patterns: [path.resolve(__dirname, './src/css/config.less')]
});
};
const getPages = () => {
// 多页面配置,相当于配置html-webpack-plugin
const pages = {};
glob.sync('./src/page/**/*.[jt]s?(x)').forEach(item => {
const filename = item.match(/\.\/src\/page\/(\S*).[jt]s?x?/)[1];
pages[filename] = {
title,
entry: item,
template: `public/${filename}.html`,
filename: `${filename}`,
chunks: ['common', filename],
chunksSortMode: 'manual',
inject: true
};
});
return pages;
};
module.exports = {
pages: getPages(),
outputDir: './static', //执行build时会在static下生成编译后的文件
assetsDir: './',
filenameHashing: false,
productionSourceMap: false,
configureWebpack: {
resolve: {
alias: {
'@css': path.join(__dirname, 'src/css'),
'@router': path.join(__dirname, 'src/router'),
'@component': path.join(__dirname, 'src/components'),
'@page': path.join(__dirname, 'src/page')
}
}
},
chainWebpack: config => {
// 移除 preload 插件
config.plugins.delete('preload');
config.plugins.delete('prefetch');
// 增加图片超过80k就转成base64编码,否则就为原图片
config.module
.rule('images')
.use('url-loader')
.loader('url-loader')
.tap(options => Object.assign(options, { limit: 1024 * 80, publicPath: '../../' }));
// 压缩图片
config.module
.rule('images')
.use('image-webpack-loader')
.loader('image-webpack-loader')
.options({
quality: '65-80'
})
.end();
// 每个单文件组件和less文件中导入config.less
const types = ['vue-modules', 'vue', 'normal-modules', 'normal'];
types.forEach(type => addStyleResource(config.module.rule('less').oneOf(type)));
// 加入ts
config.module.rule('ts').use('babel-loader');
// 移动端 px2rem-loader
config.module
.rule('less')
.oneOf('normal')
.use('px2rem-loader')
.loader('px2rem-loader')
.before('postcss-loader')
.options({
remUnit: 37.5,
remPrecision: 8
})
.end();
},
css: {
// 是否开启支持 foo.module.css 样式
modules: false,
// 是否使用 css 分离插件 ExtractTextPlugin,采用独立样式文件载入,不采用 <style> 方式内联至 html 文件中
// 如果为true,iview的icon无法显示
extract: false,
// 是否构建样式地图,false 将提高构建速度
sourceMap: false,
// css预设器配置项
loaderOptions: {
css: {},
postcss: {}
}
},
// 配置webpack-dev-server
devServer: {
open: true,
host: '127.0.0.1',
port: 3000,
https: false,
hotOnly: false,
proxy: null,
before: app => {
// 将路由加上路径前缀,否则刷新页面就会空白
// const base = '/page1/index1'.replace(/\/+$/, ''); // 移除尾部斜杠
// app.get(`${base}/:page/*`, (req, _, next) => {
// if (['router1', 'router2', 'router3'].includes(req.params.page)) {
// // 把 /<base>/<page>/* 重定向到 /<base>/<page>/
// req.url = `${base}/${req.params.page}/`;
// next('route');
// } else {
// next();
// }
// });
}
},
// 构建时开启多进程处理 babel 编译
parallel: require('os').cpus().length > 1
};
问题的记录
- 多页面配置一直失败,filename 的路径一直访问不到,原因是 publicPath 写成了相对路径./,改为默认的/即可
- 查看内置配置项的具体配置 vue inspect --rule 要查询的 rule
- css 无法加载出背景图片,在配置 url-loader 中加入 publicPath: '…/…/'即可
- ts 没有识别全局定义的 T,在 shims-tsx.d.ds 中加入
declare global {
namespace T {
const add: Function
}
}
- 在配置里加了 router 的 alias,但是会报错@router 没有定义,在 tsconfig.js 加入
"paths": {
"@/*": [
"src/*"
],
"@router/*" :["src/router/*"]
},
-
iview 的 icon 没有生效,修改 vue.config.js 中的 css:{extract: false},但是会使 css 在页面内联 style
-
加入 Row、Col 组件 eslint 报错,原因是渲染成 html 标签本身是闭合标签和 iview 标签不匹配,所以在.eslintrc 关闭该条匹配
"rules": {
"x-invalid-end-tag": false
}
- 路由传参不起作用,配置路由时加上
props: { default: true, sidebar: true },
- 将单页面的#去掉,让路由表现得像多页面,在实例化 router 时加入 mode:history,但是刷新后嵌套路由中的东西会失效,当前的做法是定义一个获取导航栏的 map,然后在 TopMenu 和 SideMenu 中循环获取当前的一级导航和二级导航列表
const router = new VueRouter({
base: '/page1/index1',
mode: 'history',
routes
});
- 在切换一级导航时,二级导航的列表没有切换,原因是 route 变化了但是列表没有及时更新,所以在 watch 中监听$route,有变化了就及时更新列表
watch: {
$route(to, from) {
this.getSideMenuList(to.name);
}
},
mounted() {
this.getSideMenuList(this.$route.name);
},