Node —— 环境安装(需要用到 npm 或 pnpm)
官网链接:http://nodejs.cn/
介绍:Node.js 是一个开源的、跨平台的 JavaScript 运行时环境
Vue-cli5 Drop support of Node.js 8-11 and 13
版本要求:Node >= v14,项目推荐使用 Node >= v16.16.0
Element-ui —— 前端 UI 框架
Vue —— 前端 MVVM 框架
Vue 生态圈
Webpack —— 打包工具
Sass —— CSS 预处理器
echarts —— 图表工具
$ npm install
$ npm run dev # run all
$ npm run dev:admin # run one -> admin
$ npm run dev:other # run one -> other
$ npm run build # build all
$ npm run build:admin # build one -> admin
$ npm run build:other # build one -> other
首字母大写:PascalCase。短横线:kebab-case
项目命名:全部 kebab-case 命名
比如:my-project
组件命名:所有组件使用大写开头 PascalCase 命名规范
比如:@/components/BackToTop/index.vue
文件命名:所有 .js
都是用 kebab-case(例外:hooks 使用 PascalCase)
比如:@/utils/des-base64.js
目录命名:所有目录文件那使用 kebab-case,有复数结构要采用复数命名法
比如:scripts
、styles
、components
、images
、views
、utils
页面命名:在 src/views
中,代表路由的 .vue
文件都是用 kebab-case
比如:@/views/svg-icons/index.vue
Git 每次提交代码时加上手写 提交说明(Commit message):
可以参考:vuejs Pull requests
Git 提交内容需要包含(做一个简化):type
(类型)、commit
(详细信息)
示例:fix: 解决xxx出现的bug
type
用于说明 commit
提交性质,通常值为下表:
值 | 描述 |
---|---|
feat | 新增一个功能 |
fix | 修复一个 Bug |
docs | 文档变更 |
style | 代码格式(修复 Eslint 报错) |
refactor | 代码重构 |
perf | 改善性能 |
test | 测试 |
build | 变更项目构建或外部依赖(webpack、npm) |
ci | 更改持续集成软件的配置文件和 package.json 中的 scripts 命令 |
chore | 变更构建流程或辅助工具 |
revert | 代码回退 |
VSCode 开发和 WebStorm 开发最好都安装 Prettier 这个插件,根目录配置 prettierrc.js
module.exports = {
// 一行最多 120 字符
printWidth: 120,
// 使用 2 个空格缩进
tabWidth: 2,
// 不使用缩进符,而使用空格
useTabs: false,
// 行尾不需要有分号
semi: false,
// 使用单引号
singleQuote: true,
// jsx 不使用单引号,而使用双引号
jsxSingleQuote: true,
// jsx 标签的反尖括号需要换行
bracketSameLine: false,
// 末尾不需要有逗号
trailingComma: 'none',
// 大括号内的首尾需要空格
bracketSpacing: true,
// 对象的 key 仅在必要时用引号
quoteProps: 'as-needed',
// 箭头函数,只有一个参数的时候,不需要括号
arrowParens: 'avoid',
// 所有元素间的空格都会被忽略,直接另起一行
htmlWhitespaceSensitivity: 'ignore',
// vue 文件中的 script 和 style 内不用缩进
vueIndentScriptAndStyle: false,
// 换行符使用 lf
endOfLine: 'lf',
// 格式化模板字符串里的内容
embeddedLanguageFormatting: 'auto',
// html, vue, jsx 中每个属性占一行
singleAttributePerLine: false,
// 使用默认的折行标准
proseWrap: 'preserve'
}
涉及的比较多,遵循 .eslintrc.js
里面的规范
执行 npm run build
,plugin 报错信息
Error: Cannot call .tap() on a plugin that has not yet been defined. Call plugin('preload')
参考:https://cli.vuejs.org/zh/config/#pages 这里面的提示
试图修改 html-webpack-plugin
和 preload-webpack-plugin
插件的选项,需要额外处理
$ npm install @vue/preload-webpack-plugin -D
+ "@vue/preload-webpack-plugin": "^2.0.0"
之前直接使用 tap 连接即可,现在需要指定 plugin,Vue-cli4 的配置如下:
Object.keys(pages).forEach(pageName => {
config.plugin(`preload-${pageName}`).tap(() => [
{
rel: 'preload',
fileBlacklist: [/\.map$/, /hot-update\.js$/, /runtime\..*\.js$/],
include: 'initial'
}
])
config.plugins.delete(`prefetch-${pageName}`)
config
.plugin('ScriptExtHtmlWebpackPlugin')
.after('html-' + pageName)
.use('script-ext-html-webpack-plugin', [
{
inline: /runtime\..*\.js$/
}
])
.end()
})
Vue-cli5 里不能使用 ScriptExtHtmlWebpackPlugin
/** vue.config.js */
Object.keys(pages).forEach(pageName => {
config.plugin(`preload-${pageName}`).use(require('@vue/preload-webpack-plugin'), [
{
rel: 'preload',
fileBlacklist: [/\.map$/, /hot-update\.js$/, /runtime\..*\.js$/],
include: 'initial'
}
])
config.plugins.delete(`prefetch-${pageName}`)
})
由于内网可能无法访问外网问题,可以搭建 npm 私库 nexus
在 vue.config.js
的 chainWebpack
或 configureWebpack
中对 externals
配置小外部使用的包
/** vue.config.js */
const isProduction = process.env.NODE_ENV === 'production'
const assetsCDN = {
echarts: 'echarts'
}
module.exports = {
configureWebpack: {
externals: isProduction ? assetsCDN.externals : {}
}
}
在各个目录下的 index.html
里引入对应的包(public.html
只是向导页与其他页面无关,可以不引入)
<!-- public/index.html -->
<script src="https://cdn.bootcdn.net/ajax/libs/echarts/4.8.0/echarts-en.common.min.js"></script>
但是如果想增加一个 CDN 链接就需要改两个文件,这样不是很好,所以可以将 JS 链接和 externals
要提取的文件名都放在一个对象里面
/** vue.config.js */
const isProduction = process.env.NODE_ENV === 'production'
const assetsCDN = {
externals: {
vue: 'Vue',
'vue-router': 'VueRouter',
vuex: 'Vuex',
axios: 'axios'
},
js: [
'//cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.min.js',
'//cdn.jsdelivr.net/npm/vue-router@3.6.4/dist/vue-router.min.js',
'//cdn.jsdelivr.net/npm/vuex@3.6.2/dist/vuex.min.js',
'//cdn.jsdelivr.net/npm/axios@0.27.2/dist/axios.min.js'
]
}
module.exports = {
configureWebpack: {
externals: isProduction ? assetsCDN.externals : {}
},
chainWebpack: config => {
if (isProduction) {
Object.keys(pages).forEach(pageName => {
config.plugin(`html-${pageName}`).tap(args => {
args[0].cdn = assetsCDN
return args
})
})
}
}
}
之后通过 webpack + ejs 自动写入到 public/index.html
即可
<!-- public/index.html -->
<% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.js) { %>
<script type="text/javascript" src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
<% } %>
Terser 里面其它配置可以参考:https://github.com/terser/terser#minify-options
删除 console,可以使用 terser
/** vue.config.js */
config.optimization.minimizer('terser').tap(options => {
// 注释console.*
options[0].terserOptions.compress.drop_console = true
// debugger
options[0].terserOptions.compress.drop_debugger = true
// 全部注释
args[0].terserOptions.output = {
comments: false
}
return options
})
或是使用 babel 去掉 console,需要安装 babel-plugin-transform-remove-console
$ npm install babel-plugin-transform-remove-console -D
更改 babel.config.js
babel-plugin-dynamic-import-node
/** babel.config.js */
module.exports = {
presets: ['@vue/cli-plugin-babel/preset'],
env: {
development: {
plugins: ['dynamic-import-node']
},
production: {
plugins: ['transform-remove-console']
}
}
}
vue-element-admin 根据体积大小、共用率、更新频率重新划分包,对代码进行如下的划分:
基础类库:chunks-libs
vue
+ vue-router
+ vuex
+ axios
,它的升级频率不高,但每个页面都需要它们UI 组件库:chunk-elementUI
Element
gzip 压缩后还得接近 200kb,UI 组件库的更新频率也会比 libs 高一点,建议 UI 组件库也单独拆成一个包自定义组件/函数:chunk-commons
app.js
中低频组件
echarts
等,大于 30kb,默认会打包具体使用它的页面 bundle 中/** vue.config.js */
config.when(isProduction, config => {
// 分包策略
config.optimization.splitChunks({
chunks: 'all',
cacheGroups: {
libs: {
name: 'chunk-libs',
test: /[\\/]node_modules[\\/]/,
priority: 10,
chunks: 'initial' // 只打包初始时依赖的第三方
},
elementUI: {
name: 'chunk-elementUI',
priority: 20, // 权重要大于 libs 和 app 不然会被打包进 libs 或者 app
test: /[\\/]node_modules[\\/]_?element-ui(.*)/
},
commons: {
name: 'chunk-commons',
test: resolve(`src/components`),
minChunks: 3, // 最小共用次数
priority: 5,
reuseExistingChunk: true
}
}
})
// 将runtime代码单独打包
config.optimization.runtimeChunk('multiple')
})
之前 index.html
的标题是通过 ejs 设置的
<%= webpackConfig.name %>
打包写入进去的<%= htmlWebpackPlugin.options.title %>
打包写入进去的<title><%= webpackConfig.name %></title>
<!-- or -->
<title><%= htmlWebpackPlugin.options.title %></title>
/** vue.config.js */
const name = 'XX管理系统'
module.exports = {
configureWebpack: {
name: name, // webpack
plugins: [
new HtmlWebpackPlugin({
title: name, // HTMLWebpackPlugin
})
]
}
}
但是使用这个方法比较难做到国际化
router.beforeEach
里设置了如果需要考虑 IE 兼容
.browserslistrc
文件里的 vue.config.js
文件里添加 transpileDependencies: true
可以这个网站进行查询:https://caniuse.com/
flex 布局不要使用 justify-content: space-evenly
(不支持 Chorme 56 版本以下)
在不给 display: flex
的情况下直接给 flex: 1
在低版本浏览器会造成高度不能自动撑开(原因:flex-grow: 1
没生效)
.table-wrapper {
display: flex;
flex-direction: column;
flex: 1;
}
绝对定位 position: absolute
需要把 left/right
和 top/bottom
都写上,不然在低版本浏览器会出现位置错乱情况
flex
布局且知道宽高时,可以使用 left + ﹣margin-left
和 top + -margin-top
.login-shadow {
position: absolute;
width: 80rem * 1.06;
height: 37.5rem * 1.06;
left: 50%;
top: 50%;
margin-left: -80rem * 1.06 / 2;
margin-top: -37.5rem * 1.06 / 2;
/* transform: translate(-50%, -50%); */
}
IE11 CSS3 样式部分会出现问题
crypto-js 缺少 ")"
crypto-js
由 4.x 版本切换至 3.x 版本SyntaxError: 语法错误 ./node_modules/jsencrypt/bin/jsencrypt.js
jsencrypt
由 3.x 版本切换至 3.0 版本项目已经使用了 @vue/cli-plugin-babel/preset
已经对代码进行了 babel 转换
transpileDependencies: true
使用可选链(?.
)打包报错,空值合并运算符(??
)同理
使用对应新语法打包失败,需要安装的参考 babel 官网https://babeljs.io/docs/babel-preset-env
安装 @babel/plugin-transform-optional-chaining
$ npm install @babel/plugin-transform-optional-chaining -D
配置 babel.config.js
module.exports = {
plugins: ["@babel/plugin-transform-optional-chaining"]
}
下载 Blob 文件兼容 IE
使用 window.navigator.msSaveBlob
判断一下
if (window.navigator.msSaveBlob) {
try {
window.navigator.msSaveBlob(blob, fileName) // ie 浏览器
} catch (e) {
// console.log(e)
}
} else {
const URL = window.URL || window.webkitURL
const link = document.createElement('a')
const href = URL.createObjectURL(blob)
link.href = href
link.download = fileName
link.click()
window.URL.revokeObjectURL(href)
}
虽然前后端分离十分流行,但是前后端一起使用 Maven 进行打为 jar 包的项目也是个较为常见的作法
需要在根目录添加 pom.xml
即可
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!--工父程相关信息,属于哪个组-->
<groupId>com.myapp.base</groupId>
<!-- 项目所在组中的唯一 ID -->
<artifactId>myapp-base-ui</artifactId>
<!-- 打包类型:做为子项目打成 jar 包 -->
<packaging>jar</packaging>
<version>1.0.0-SNAPSHOT</version>
<properties>
<java.version>1.8</java.version>
</properties>
<build>
<resources>
<resource>
<directory>package/main/resources</directory>
<filtering>false</filtering>
<includes>
<include>**/*.*</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<filesets>
<fileset>
<directory>package/main/resources</directory>
<includes>
<include>**/*</include>
</includes>
<followSymlinks>false</followSymlinks>
</fileset>
</filesets>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<id>copy-resources</id>
<phase>validate</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<encoding>UTF-8</encoding>
<outputDirectory>package/main/resources/static</outputDirectory>
<resources>
<resource>
<directory>dist</directory>
<filtering>false</filtering>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
只主项目里或其它子项目里直接引入 web 项目即可
<dependency>
<groupId>com.myapp.base</groupId>
<artifactId>myapp-base-ui</artifactId>
<version>${myapp.base.version}</version>
</dependency>
在 spring 拦截器里,需要再指定一下静态文件目录
@Configuration
public class ResourcesConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
/** swagger配置 */
registry.addResourceHandler("/swagger-ui/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/");
/** web静态文件配置 */
registry.addResourceHandler("/static/**")
.addResourceLocations("classpath:/static/");
}
}
css 全局变量
pretendData
改为 additionalData
/** vue.config.js */
css: {
loaderOptions: {
sass: {
additionalData: '@import "~@/styles/variables.scss";'
}
}
}
再升级 sass 版本 和 sass-loader 版本,需要同时升级,不然会出现警告
::v-deep
$ npm i sass sass-loader@12
- "sass": "1.26.2"
- "sass-loader": "8.0.2"
+ "sass": "^1.44.0"
+ "sass-loader": "^12.6.0"
这个一定要解决
warning in ./src/pages/xx/components/layout/components/Sidebar/index.vue?vue&type=script&lang=js&
export 'default' (imported as 'variables') was not found in '@/styles/variables.scss' (module has no exports)
因为页面里用到了 variables.scss
导出的变量,新版如果没有进行处理会导致页面阻塞
variables.scss
名改为 variables.module.scss
官方文档:https://cli.vuejs.org/migrations/migrate-from-v4.html
- 可以先使用
vue upgrade
命令自动升级,一般会有如下几个文件发生修改
补充上面没有提到的内容
之前使用 JSDoc 的形式可以改为 defineConfig
帮手函数,提示更加友好
/**
* @type {import('@vue/cli-service').ProjectOptions}
*/
module.exports = { }
// 需要改为如下内容
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({ })
配置的问题比较好改,可以先看下官方文档。简单改完之后使用 npm run dev
启动看报错提示即可,报错信息很详细,遗漏的很快就能改好
ValidationError: Invalid options object. Dev Server has been initialized using an options
object that does not match the API schema.
- options has an unknown property 'disableHostCheck'. These properties are valid:
object { allowedHosts?, bonjour?, client?, compress?, devMiddleware?, headers?, historyApiFallback?, host?, hot?, http2?, https?, ipc?, liveReload?, magicHtml?, onAfterSetupMiddleware?, onBeforeSetupMiddleware?, onListening?, open?, port?, proxy?, server?, setupExitSignals?, setupMiddlewares?, static?, watchFiles?, webSocketServer? }
比如:这里报错 disableHostCheck
是未知属性,就可以在这个 文档 中查一下,看看它改成了什么
disableHostCheck
option was removed in favor allowedHosts: 'all'
devServer: {
disableHostCheck: true
}
// 需要改为
devServer: {
allowedHosts: "all"
}
devtool
更加严格,填写之前去 webpack 官网查一下:
https://www.webpackjs.com/configuration/devtool/
ValidationError: Invalid configuration object. Webpack has been initialized using a configuration object that does not match the API schema.
- configuration.devtool should match pattern "^(inline-|hidden-|eval-)?(nosources-)?(cheap-(module-)?)?source-map$".
BREAKING CHANGE since webpack 5: The devtool option is more strict.
Please strictly follow the order of the keywords in the pattern
比如:你写 cheap-module-eval-source-map
是不合法的
eval-cheap-module-source-map
config.when(process.env.NODE_ENV === 'development', config => config.devtool('eval-cheap-module-source-map'))
官网没有提及的插件:
svg-sprite-loader
4 版本会报错
ERROR in ./src/pages/xx/icons/svg/wechat.svg
Module build failed (from ./node_modules/svg-sprite-loader/lib/loader.js):
Error: Cannot find module 'webpack/lib/RuleSet'
升级 svg-sprite-loader
即可
$ npm i svg-sprite-loader@6
- "svg-sprite-loader": "4.1.3"
+ "svg-sprite-loader": "^6.0.11"
打包两次问题,Vue-cli5 以后你会发现会打包两次
- Building legacy bundle for production...
- Building module bundle for production...
主要是因为要兼容浏览器导致,可以在 .browserslistrc
里配置 not dead
> 1%
last 2 versions
not dead
再进行打包就只会打包一次
- Building for production...
其他问题
页面报错:ReferenceError: process is not defined
vue-element-admin
架子里有 SidebarItem.vue
这个文件
resolvePath
方法,里面使用了 path.resolve
path
模块解决方法:自己实现一下 path.resolve
方法,并替换进去即可,不推荐使用 path-browserify
来替代path
模块
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。