前端 vue项目构建流程技术指导
为保证前端人员构建vue工程项目的流程化、规范化、统一化,并有效提升前端人员从零搭建项目的能力,特建立《前端vue项目构建流程技术指导》文档,前端人员不借助骨架项目构建项目的时候,应按照该指导说明文档进行项目构建,从而保证构建项目的一致性、全面性和规范性。该指导文档包括统一开发工具(HBuilder X)、统一代码检测工具、统一格式化配置、统一 node 版本、统一 sass 版本、统一通用型依赖安装(ui组件库、vuex、vueRouter、axios等),统一目录结构、统一 http 请求封装。该文档还包括从创建项目、依赖安装、相关配置、目录结构等详细的构建流程,以及一些公共性问题的处理方案。
PDF文档
目录
一、前端环境搭建
1.1 nodejs 安装
1.下载nodejs 安装程序
下载地址:CNPM Binaries Mirror (npmmirror.com). 18.1/)
根据系统下载对应的安装包 ,node版本,请统一选择v14.16.1
安装时建议修改安装目录,建议放到非C盘目录下。
安装完成后启动命令行工具,输入node -v npm -v查看安装版本,出现提示版本信息即为安装成功。
2.环境变量配置
这里的环境变量配置主要的是npm安装的全局模块所在的路径,以及缓存cache的路径,如果不配置在执行类似:`npm install 模块名 [-g]`的安装语句时,会将安装的模块安装到【C:\Users\用户名\AppData\Roaming\npm】路径中,占用C盘空间。
本文将nodejs安装在D:\soft\nodejs 目录下,以下操作可以根据实际安装目录情况进行对应的调整。
- 在安装目录下,如
D:\soft\nodejs新建两个文件夹node_global(全局包存放目录) 和node_cache(缓存目录); - 打开命令行工具,执行以下两句操作:
npm config set prefix "D:\soft\nodejs\node_global"``npm config set cache "D:\soft\nodejs\node_cache"; - 配置环境变量:
- 打开系统属性-高级-环境变量,在系统变量中新建 变量名:
NODE_PATH,变量值:D:\soft\nodejs\node_global\node_modules(见图2); - 编辑用户变量的
path,将默认的C盘下APPData/Roaming\npm修改为D:\soft\nodejs\node_global(见图3); - 保存即可。
1.2 安装 yarn
因为 yarn 具有优势,开发中推荐使用 yarn 来替代 npm 进行操作。安装 yarn 可以下载安装包进行安装,也可以使用 npm 安装:
npm install yarn -g
1.2.1 yarn的常用命令
# 1.安装包,类似于npm installyarn# 2.安装某个包,类似于npm install vue --saveyarn add vue# 3.卸载某个包,类似于npm uninstall vue --saveyarn remove vue# 4.安装到开发依赖,类似于npm install vue --save-devyarn add vue --dev# 5.更新包yarn upgrade #更新包到基于规范范围的最新版本yarn upgrade --latest # 忽略版本规则,升级到最新版本,并且更新 package.json
1.2.2镜像源管理器
yrm是yarn源的管理器,用于快速切换镜像源
# 安装yrmyarn add -g yrm# 查看当前可用源yrm ls# 切换源yrm use taobao# 查看yarn当前镜像源yarn config get registry# 添加源yarn add npmhzwq http://192.168.14.25:8081/repository/npm-all/# 删除源yrm del npmhzwq# 测试源响应时间yrm test
想了解cnpm以及npm的源管理器nrm可以查看8.2关于cnpm及nrm介绍.
二、项目创建
2.1 安装 Vue CLI
执行下列命令安装Vue CLI
yarn global add @vue/cli@5.0.3 # yarn方式npm install -g @vue/cli@5.0.3 # npm方式
安装之后,你就可以在命令行中访问 vue 命令。你可以通过简单运行 vue,看看是否展示出了一份所有可用命令的帮助信息,来验证它是否安装成功。
你还可以用这个命令来检查其版本是否正确:
vue -V
如需升级项目中的 Vue CLI 相关模块(以 @vue/cli-plugin- 或 vue-cli-plugin- 开头),请在项目目录下运行 vue upgrade:
用法: upgrade [options] [plugin-name](试用)升级 Vue CLI 服务及插件选项:-t, --to <version> 升级 <plugin-name> 到指定的版本-f, --from <version> 跳过本地版本检测,默认插件是从此处指定的版本升级上来-r, --registry <url> 使用指定的 registry 地址安装依赖--all 升级所有的插件--next 检查插件新版本时,包括 alpha/beta/rc 版本在内-h, --help 输出帮助内容
2.2 创建项目
在要创建项目的路径下使用命令行创建项目
vue create hello-world
会被提示选取一个preset。这里选择基本的Babel + ESLint 设置的preset。

使用键盘上下方向键定位到 Default([Vue 2] babel, eslint),然后回车

如图,项目创建完成
执行
cd hello-world
进入新建项目,
运行项目:
yarn serve #yarn方式npm run serve #npm方式

在浏览器中访问 http://localhost:8080 或http://192.168.23.123:8080

至此,基础的基于 @vue/cli 的项目创建完成。
项目中 package.json 文件内容:
{"name": "hello-world","version": "0.1.0","private": true,"scripts": {"serve": "vue-cli-service serve","build": "vue-cli-service build","lint": "vue-cli-service lint"},"dependencies": {"core-js": "^3.8.3","vue": "^2.6.14"},"devDependencies": {"@babel/core": "^7.12.16","@babel/eslint-parser": "^7.12.16","@vue/cli-plugin-babel": "~5.0.0","@vue/cli-plugin-eslint": "~5.0.0","@vue/cli-service": "~5.0.0","eslint": "^7.32.0","eslint-plugin-vue": "^8.0.3","vue-template-compiler": "^2.6.14"},"eslintConfig": {"root": true,"env": {"node": true},"extends": ["plugin:vue/essential","eslint:recommended"],"parserOptions": {"parser": "@babel/eslint-parser"},"rules": {}},"browserslist": ["> 1%","last 2 versions","not dead"]}
三、依赖安装
3.1 安装sass sass-loader:
yarn add sass -Dyarn add sass-loader -D
若安装依赖报错,请参考8.4章节.
3.2 安装vuex :
yarn add vuex@3.6.2
对于大型应用,我们推荐 Vuex 相关代码按业务模块进行划分管理。
1.在src目录下创建如下目录结构:
└── store├── index.js # 我们组装模块并导出 store 的地方└── modules├── menu.js # 模块示例:菜单模块└── user.js # 模块示例:用户模块
2.src\index.js 书写demo:
import Vue from 'vue';import Vuex from 'vuex';//导入user模块import user from './modules/user'import menu from './modules/menu'Vue.use(Vuex)//创建vuex实列export default new Vuex.Store({//模块化创建moudles:{user,menu}})
- user.js 模块书写demo
//导入axiosimport axios from 'axios'export default {//开启命名空间一定要写这个namespaced:true,//state数据状态定义state(){profile:{user:'小明', //用户名token:null //用户的token}},//vuex方法// mutations 定义方法mutations:{//定义一个修改用户的方法函数setUser(state,profile){state.profile = profile //profile 是外界传的参数}},//请求数据的地方 如果要请求一个数据可以这样来写这个是补充的// actions 定义方法actions:{//调用axios的请求函数async getUserInfo(context){const{ data:data} = await axios.get('请求的路径地址',{请求的参数})console.log(data) //服务器返回的数据context.commit('setUser', data)}}}
在src\main.js 中引入 store\index.js 以使用 vuex:
import Vue from 'vue'import App from './App.vue'// vueximport store from './store'Vue.config.productionTip = falsenew Vue({store, // 使用storerender: h => h(App),}).$mount('#app')
配置路径别名,方便组件引入
在vue.config.js 中配置路径别名:
const { defineConfig } = require('@vue/cli-service')const path = require('path')const resolve = (dir) => path.join(__dirname, dir)module.exports = defineConfig({transpileDependencies: true,chainWebpack: config => {// 配置别名config.resolve.alias.set('@', resolve('src')).set('@views', resolve('src/views'))}})
!! 修改 vue.config.js 后 需要
Ctrl + C停止项目,然后yarn serve运行项目才能看到修改后的效果。!!
3.3 安装vue-router
yarn add vue-router@3.5.3
在 src 目录下创建 router 文件夹,
在 router 文件夹中新建 index.js,routes.js。
src\router\index.js:
import Vue from 'vue';import Router from 'vue-router';import routes from './routes';Vue.use(Router);const router = new Router({mode: 'history',routes});export default router;
src\router\routes.js:
const DashBoard = () => import('@views/dash-board.vue');const DemoPage = () => import('@views/demo-page.vue');const routes = [{path: '/dashboard',name: 'DashBoard',component: DashBoard},{path: '/demo',name: 'DemoPage',component: DemoPage}];export default routes;
注意:routes 的 name 名称要使用大驼峰命名,并与组件定义的 name 名称保持一致。
对应的在 src 目录中创建 views 文件夹,
在 src\views 文件夹中创建 dash-board.vue demo-page.vue。
注意:文件名采用小写中划线拼接,vue文件中必须定义 name 属性,并且和 routes 的 name 保持一致。
在 main.js 中引入 router.js:
import Vue from 'vue'import App from './App.vue'// vueximport store from './store'// vue-routerimport router from './router'Vue.config.productionTip = falsenew Vue({store, // 使用storerouter, // 使用vue-routerrender: h => h(App),}).$mount('#app')
在地址栏输入 http://localhost:8080/dashboard 发现页面并没有跳转。
在 App.vue 中添加 <router-view></router-view>:
<template><div id="app"><img alt="Vue logo" src="./assets/logo.png" /><!-- <HelloWorld msg="Welcome to Your Vue.js App" /> --><ul><li><router-link to="/">首页</router-link></li><li><router-link to="/dashboard">DashBoard</router-link></li><li><router-link to="/demo">DemoPage</router-link></li></ul><router-view></router-view></div></template><script>// import HelloWorld from "./components/HelloWorld.vue";export default {name: "App",components: {// HelloWorld,},};</script><style>#app {font-family: Avenir, Helvetica, Arial, sans-serif;-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;text-align: center;color: #2c3e50;margin-top: 60px;}</style>
再次访问 http://localhost:8080/dashboard :

访问 `http://localhost:8080/demo`:

3.4 安装element-ui:
yarn add element-ui
3.4.1 全量引入element-ui
在 main.js 中全局引入 element-ui:
import Vue from 'vue'import App from './App.vue'// 全局引入 element-uiimport ElementUI from 'element-ui'// 引入 element-ui 样式import 'element-ui/lib/theme-chalk/index.css'// vueximport store from './store'// vue-routerimport router from './router'// 配置默认尺寸 small z-index层级 3000Vue.use(ElementUI, {size: 'small',zIndex: 3000})Vue.config.productionTip = falsenew Vue({store, // 使用storerouter, // 使用vue-routerrender: h => h(App),}).$mount('#app')
使用 elment-ui 中的 el-link 替换 router-link :
<template><div id="app">...<ul><li><!-- <router-link to="/">首页</router-link> --><el-link href="/" type="primary">首页</el-link></li><li><!-- <router-link to="/dashboard">DashBoard</router-link> --><el-link href="/dashboard" type="success">DashBoard</el-link></li><li><!-- <router-link to="/demo">DemoPage</router-link> --><el-link href="/demo" type="info">DemoPage</el-link></li></ul>...</div></template>
验证效果:

3.4.2 按需引入 element-ui: [推荐]
借助 babel-plugin-component,我们可以只引入需要的组件,以达到减小项目体积的目的。
1.首先,安装 babel-plugin-component:
yarn add babel-plugin-component @babel/preset-env -D
2.然后修改 babel.config.js:
module.exports = {presets: ['@vue/cli-plugin-babel/preset',['@babel/preset-env', { modules: false }]],plugins: [['component',{'libraryName': 'element-ui','styleLibraryName': 'theme-chalk'}],"@babel/plugin-transform-runtime"]}
3.创建 src/utils/element.js 文件,下面列举了所有的组件,在项目中只选择需要用到的组件即可:
import Vue from 'vue'import {Pagination,Dialog,Divider,Autocomplete,Dropdown,DropdownMenu,DropdownItem,Menu,Submenu,MenuItem,MenuItemGroup,Input,InputNumber,Radio,RadioGroup,RadioButton,Checkbox,CheckboxButton,CheckboxGroup,Switch,Select,Option,OptionGroup,Button,ButtonGroup,Table,TableColumn,DatePicker,TimeSelect,TimePicker,Popover,Tooltip,Breadcrumb,BreadcrumbItem,Form,FormItem,Tabs,TabPane,Tag,Tree,Timeline,TimelineItem,Link,Alert,Slider,Icon,Row,Col,Upload,Progress,Badge,Card,Rate,Scrollbar,Steps,Step,Carousel,CarouselItem,Collapse,CollapseItem,Cascader,ColorPicker,Transfer,Container,Header,Aside,Main,Footer,Loading,MessageBox,Message,Notification} from 'element-ui'Vue.use(Pagination)Vue.use(Dialog)Vue.use(Divider)Vue.use(Autocomplete)Vue.use(Dropdown)Vue.use(DropdownMenu)Vue.use(DropdownItem)Vue.use(Menu)Vue.use(Submenu)Vue.use(MenuItem)Vue.use(MenuItemGroup)Vue.use(Input)Vue.use(InputNumber)Vue.use(Radio)Vue.use(RadioGroup)Vue.use(RadioButton)Vue.use(Checkbox)Vue.use(CheckboxButton)Vue.use(CheckboxGroup)Vue.use(Switch)Vue.use(Select)Vue.use(Option)Vue.use(OptionGroup)Vue.use(Button)Vue.use(ButtonGroup)Vue.use(Table)Vue.use(TableColumn)Vue.use(DatePicker)Vue.use(TimeSelect)Vue.use(TimePicker)Vue.use(Popover)Vue.use(Tooltip)Vue.use(Breadcrumb)Vue.use(BreadcrumbItem)Vue.use(Form)Vue.use(FormItem)Vue.use(Tabs)Vue.use(TabPane)Vue.use(Tag)Vue.use(Tree)Vue.use(Timeline)Vue.use(TimelineItem)Vue.use(Link)Vue.use(Alert)Vue.use(Slider)Vue.use(Icon)Vue.use(Row)Vue.use(Col)Vue.use(Upload)Vue.use(Progress)Vue.use(Badge)Vue.use(Card)Vue.use(Rate)Vue.use(Scrollbar)Vue.use(Steps)Vue.use(Step)Vue.use(Carousel)Vue.use(CarouselItem)Vue.use(Collapse)Vue.use(CollapseItem)Vue.use(Cascader)Vue.use(ColorPicker)Vue.use(Transfer)Vue.use(Container)Vue.use(Header)Vue.use(Aside)Vue.use(Main)Vue.use(Footer)Vue.use(Loading.directive)Vue.prototype.$loading = Loading.serviceVue.prototype.$msgbox = MessageBoxVue.prototype.$alert = MessageBox.alertVue.prototype.$confirm = MessageBox.confirmVue.prototype.$prompt = MessageBox.promptVue.prototype.$notify = NotificationVue.prototype.$message = MessageVue.prototype.$ELEMENT = {size: 'small',zIndex: 3000};// element组件库的Dialog对话框默认可以通过点击 modal 关闭 Dialog,即点击空白处弹框可关闭。Dialog.props.closeOnClickModal.default = false;
在 main.js 中修改全量引入为按需引入:
import Vue from 'vue'import App from './App.vue'// vueximport store from './store'// vue-routerimport router from './router'// 全局引入 element-ui// import ElementUI from 'element-ui'// 引入 element-ui 样式// import 'element-ui/lib/theme-chalk/index.css'// 配置默认尺寸 small z-index层级 3000 [全量引入配置]/*Vue.use(ElementUI, {size: 'small',zIndex: 3000})*/// 按需引入 element 组件 [先]import '@/utils/element.js'// 引入 自定义 element-ui 主题样式 [后]import '../themes/index.css'// 引入全局公用样式import '@/common/style/index.scss'Vue.config.productionTip = falsenew Vue({store, // 使用storerouter, // 使用vue-routerrender: hyperscript => hyperscript(App)}).$mount('#app')
3.5 安装axios:
yarn add axios
1.封装axios:在 src 文件夹下创建 utils 文件夹,并在 utils 文件夹中新建 request.js文件,写入如下内容:
src/utils/request.js
import router from '@/router'import axios from 'axios'import { Message } from 'element-ui'// 创建 axios 实例const service = axios.create({timeout: 30000,headers: {'Content-Type': 'application/json;charset=utf-8'}})// 拦截 request 请求service.interceptors.request.use(function (config) {// 统一添加配置信息// 防止IE浏览器缓存config.headers.common['Cache-Control'] = 'no-cache'// 获取 token,若不存在,则为空const token = localStorage.getItem('token')if (token) {// 如果存在 token,则每个请求 header 都加上 tokenconfig.headers.authorization = token}return config}, function (error) {return Promise.reject(error)})// 拦截 response, 统一处理错误service.interceptors.response.use(function (response) {if (response.data && response.status === 200) {return response}return Promise.reject(new Error(response))}, function (error) {// axios 获得的响应 status code 超出了 2xx 的范围时,统一处理接口错误if (!window.navigator.onLine) {// 断网处理router.push('/error')} else {if (error && error.response) {const duration = 3000,errorStatusHash = {401: '权限不足',404: '请求路径不存在'// 500: '服务器异常'}if (error.response.status in errorStatusHash) {Message({message: errorStatusHash[error.response.status],type: 'error',duration})if (error.response.status === 500) {const errMsgHash = {S00001: '接口超时',S00002: '接口解析错误',S00003: '接口返回数据异常',S00004: '代码报错',S00005: '数据库报错'}console.log(errMsgHash[error.response.data.code] || '服务器异常')// Message({// message: '服务器异常',// type: 'error',// duration: 3 * 1000// })}} else {Message({message: '操作失败',type: 'error',duration: 3 * 1000})}}}return Promise.reject(error)})// 实例变量支持 allservice.all = axios.allexport default service
3.使用request发起api请求,并统一管理:在 src 目录下创建 api 文件夹,在api文件夹中新建 test-api.js文件,写入如下内容:
import request from '@utils/request'// get 请求export const testApi = (params) => {return request({url: '/api/v1/images/search',method: 'get',params // params: params 简写})}// post 请求export const testApiPost = (params) => {return request({url: '/api/v1/images/search',method: 'get',data: params})}
4.测试:在 src\views\demo-page.vue 中测试 封装的axiso:
<template><div class="demo-page"><h4>Demo page.</h4><img class="random-img" :src="randomImg" alt="" /></div></template><script>import { testApi } from "@api/test-api";export default {name: 'DemoPage',data() {return {randomImg: "",};},mounted() {this.initData();},methods: {initData() {const params = {greeting: "Hi!"}testApi(params).then((res) => {console.log("res: ", res);if (res.status === 200) {if (res.data && res.data.length) {this.randomImg = res.data[0].url;}}}).catch((err) => {console.log("err: ", err);});},},};</script><style lang="scss" scoped>.demo-page {text-align: center;.random-img {max-width: 50vw;}}</style>
在 vue.config.js 中添加别名配置:
// webpack 配置chainWebpack: config => {config.resolve.alias.set('@', resolve('src')).set('@views', resolve('src/views')).set('@utils', resolve('src/utils')).set('@api', resolve('src/api'))}
访问http://localhost:8080/demo:

至此
项目目录结构如下图:

!!( 图片中DashBoard.vue 、DemoPage.vue 应为 dash-board.vue 、demo-page.vue )!!
package.json中的依赖如下:
"dependencies": {"axios": "^0.26.1","core-js": "^3.8.3","element-ui": "^2.15.6","vue": "^2.6.14","vue-router": "3.5.3","vuex": "3.6.2"},"devDependencies": {"@babel/core": "^7.12.16","@babel/eslint-parser": "^7.12.16","@vue/cli-plugin-babel": "~5.0.0","@vue/cli-plugin-eslint": "~5.0.0","@vue/cli-service": "~5.0.0","eslint": "^7.32.0","eslint-plugin-vue": "^8.0.3","sass": "^1.49.9","sass-loader": "10.2.1","vue-template-compiler": "^2.6.14"},
四、定义全局公共样式
为了便于公共样式的统一管理,我们需要定义全局公共样式。
1.引入base.css文件
在main.js文件引入base.css文件
@charset "utf-8";/*主要用于样式重置base.css*//*移动端默认样式*//*清除掉按下时会有一个灰色阴影*/a,input,button{-webkit-tap-highlight-color: rgba(0,0,0,0);}/*清除掉ios自带圆角*/input,button{-webkit-appearance: none;/*消除输入框核按钮的默认外观*/border-radius: 0;}/* 禁用iPhone中Safari的字号自动调整 */html {-webkit-text-size-adjust: 100%;-ms-text-size-adjust: 100%;/* 解决IOS默认滑动很卡的情况 */-webkit-overflow-scrolling : touch;overflow-scrolling:touch;}/* 禁止缩放表单 */input[type="submit"], input[type="reset"], input[type="button"], input {resize: none;border: none;}/* 取消链接高亮 */body, div, ul, li, ol, h1, h2, h3, h4, h5, h6, input, textarea, select, p, dl, dt, dd, a, img, button, form, table, th, tr, td, tbody, article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section {-webkit-tap-highlight-color: rgba(0, 0, 0, 0);}/* 设置HTML5元素为块 */article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section {display: block;}/* 图片自适应 */img {width: 100%;height: auto;width: auto\9; /* ie8 */display: block;-ms-interpolation-mode: bicubic;/*为了照顾ie图片缩放失真*/}/* 初始化 */body, div, ul, li, ol, h1, h2, h3, h4, h5, h6, input, textarea, select, p, dl, dt, dd, a, img, button, form, table, th, tr, td, tbody, article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section {margin: 0;padding: 0;}body{/*禁止选中文字*/-webkit-user-select: none;/* iPhone 和 Android 的浏览器纵向 (Portrate mode) 和橫向 (Landscape mode) 模式皆有自动调整字体大小的功能。控制它的就是 CSS 中的 -webkit-text-size-adjust。关闭字体大小自动调整功能*/-webkit-text-size-adjust: 100%;}/*字体设置*/body *{/*每台设备里的默认字体是不一样的(移动端设备里大多数没有宋体和微软雅黑字体)*/font-family: helvetica;}em, i {font-style: normal;}ul,li{list-style-type: none;}h1,h2,h3,h4,h5,h6,strong,b,i,em{font-weight: normal; font-style: normal;}a {text-decoration: none;color: #969696;font-family: 'Microsoft YaHei', Tahoma, Arial, sans-serif;}a:hover {text-decoration: none;}ul, ol {list-style: none;}h1, h2, h3, h4, h5, h6 {font-size: 100%;font-family: 'Microsoft YaHei';}img {border: none;}input{font-family: 'Microsoft YaHei';}/* 移动端点击a链接出现蓝色背景问题解决 */a:link,a:active,a:visited,a:hover {background: none;-webkit-tap-highlight-color: rgba(0,0,0,0);-webkit-tap-highlight-color: transparent;}.clearfix:after {content: "";display: block;visibility: hidden;height: 0;clear: both;}.clearfix {zoom: 1;}
2.定义scss变量文件var.scss
在src/common/style/ 目录中,新建var.scss文件,并定义如下内容(相关变量的值可以参考项目的ui效果图主题色信息修改):
//定义单位-uniapp项目需要使用 rpx;@return $px * 1rpx;@function px($px) {@return $px * 1px;}// 颜色$color-primary: #2ffec4;$color-success: #09ad9c;$color-warning: #f8af39;$color-danger: #ff3333;$color-white: white;$color-black: black;$colors: (primary: $color-primary,success: $color-success,warning: $color-warning,danger: $color-danger,white: $color-white,black: $color-black,);//字体大小$font-mini: px(12);$font-small: px(16);$font-base: px(14);$font-large: px(18);$fonts: (mini: $font-mini,small: $font-small,base: $font-base,large: $font-large,);//border$border-width: px(1);$border-color: #e8e8e8;// border-radius$radius-base: px(4);$radius-circle: 50%;$radius: (base: $radius-base,circle: $radius-circle,);//定位$positions: (absolute: absolute,relative: relative,fixed: fixed,);//文字对齐$textAligns: (left: left,center: center,right: right,);//浮动$floats: (left: left,right: right,);//间距$size-zero: 0;$size-mini: px(10);$size-small: px(20);$size-base: px(24);$size-large: px(28);$sizes: (zero: $size-zero,mini: $size-mini,small: $size-small,base: $size-base,large: $size-large,);
3.定义重复性强的样式代码块文件mixins.scss
//字体@mixin font-family($value: ('Microsoft YaHei',Tahoma,Arial,sans-serif,)) {font-family: $value;}// 文字溢出隐藏显示...@mixin text-overflow($line: 1) {@if $line==1 {text-overflow: ellipsis;overflow: hidden;white-space: nowrap;} @else {overflow: hidden;text-overflow: ellipsis;display: -webkit-box;-webkit-line-clamp: $line;/*! autoprefixer: off */-moz-box-orient: vertical;-webkit-box-orient: vertical;}}//border@mixin border($color: $border-color, $width: $border-width) {border: $width solid $color;}@mixin border-dir($i, $width: $border-width, $color: $border-color) {border-#{$i}: $width solid $color;}// border-radius@mixin radius($radius: $radius-base, $overflow: hidden) {border-radius: $radius;overflow: $overflow;}//间距@mixin padding-col($n: $size-base, $sizeKey: none) {@if $sizeKey == 'zero' {padding-top: $n !important;padding-bottom: $n !important;} @else {padding-top: $n;padding-bottom: $n;}}@mixin padding-row($n: $size-base, $sizeKey: none) {@if $sizeKey == 'zero' {padding-left: $n !important;padding-right: $n !important;} @else {padding-left: $n;padding-right: $n;}}@mixin margin-col($n: $size-base, $sizeKey: none) {@if $sizeKey == 'zero' {margin-top: $n !important;margin-bottom: $n !important;} @else {margin-top: $n;margin-bottom: $n;}}@mixin margin-row($n: $size-base, $sizeKey: none) {@if $sizeKey == 'zero' {margin-left: $n !important;margin-right: $n !important;} @else {margin-left: $n;margin-right: $n;}}
4.新建公共样式文件global.scss文件
在 src/common/style/ 目录,新建global.scss文件,并定义全局公共样式。代码如下:
@import './var.scss';@import './mixins.scss';.font-family {@include font-family;}// 颜色@each $colorKey, $color in $colors {//字体颜色 .text-#{'' + $colorKey}用于解决警告,添加‘’强制转换为字符串.text-#{'' + $colorKey} {color: $color;}//背景颜色 .bg-#{'' + $colorKey}用于解决警告,添加‘’强制转换为字符串.bg-#{'' + $colorKey} {background-color: $color;}}//字体大小@each $fontKey, $font in $fonts {.font-#{$fontKey} {font-size: $font;}}// 文字溢出隐藏显示...//1-4行@for $i from 1 through 4 {@if $i == 1 {.line-ellipsis {@include text-overflow($i);}} @else {.line-limited#{$i} {@include text-overflow($i);}}}//border//颜色边框,.border-#{''+$borderKey} 用于解决警告,添加''强制转换为字符串@each $borderKey, $border in $colors {.border-#{'' + $borderKey} {@include border($border);}}//边框粗细@for $i from 1 through 5 {@if $i == 1 {.border {@include border;}} @else {.border#{$i} {@include border($border-color, px($i));}}}//边框方向@each $i in left, right, top, bottom {.border-#{$i} {@include border-dir($i);}}// border-radius@each $radiusKey, $radiu in $radius {.border-radius-#{$radiusKey} {@include radius($radiu);}}// 定位@each $positionKey, $position in $positions {.position-#{$positionKey} {position: $position;}}// 文字对齐@each $textAlignKey, $textAlign in $textAligns {.text-#{$textAlignKey} {text-align: $textAlign;}}//浮动@each $floatKey, $float in $floats {.float-#{$floatKey} {float: $float;}}//清除浮动.clearfix {zoom: 1;&:after {content: '';display: block;visibility: hidden;height: 0;clear: both;}}//间距@each $sizeKey, $size in $sizes {.p-#{$sizeKey} {@include padding-col($size, $sizeKey);@include padding-row($size, $sizeKey);}.p-row-#{$sizeKey} {@include padding-row($size, $sizeKey);}.p-col-#{$sizeKey} {@include padding-col($size, $sizeKey);}.pl-#{$sizeKey} {@if $sizeKey == 'zero' {padding-left: $size !important;} @else {padding-left: $size;}}.pr-#{$sizeKey} {@if $sizeKey == 'zero' {padding-right: $size !important;} @else {padding-right: $size;}}.pt-#{$sizeKey} {@if $sizeKey == 'zero' {padding-top: $size !important;} @else {padding-top: $size;}}.pb-#{$sizeKey} {@if $sizeKey == 'zero' {padding-bottom: $size !important;} @else {padding-bottom: $size;}}.m-#{$sizeKey} {@include margin-col($size, $sizeKey);@include margin-row($size, $sizeKey);}.m-row-#{$sizeKey} {@include margin-row($size, $sizeKey);}.m-col-#{$sizeKey} {@include margin-col($size, $sizeKey);}.ml-#{$sizeKey} {@if $sizeKey == 'zero' {margin-left: $size !important;} @else {margin-left: $size;}}.mr-#{$sizeKey} {@if $sizeKey == 'zero' {margin-right: $size !important;} @else {margin-right: $size;}}.mt-#{$sizeKey} {@if $sizeKey == 'zero' {margin-top: $size !important;} @else {margin-top: $size;}}.mb-#{$sizeKey} {@if $sizeKey == 'zero' {margin-bottom: $size !important;} @else {margin-bottom: $size;}}}//flex.flex-row {display: flex;}.flex-row-vcenter {display: flex;align-items: center;} /* 垂直居中对齐 */.flex-row-rcenter {display: flex;justify-content: center;} /* 水平居中对齐 */.flex-row-c {display: flex;align-items: center;justify-content: center;} /* 水平垂直居中对齐 */.flex-row-bw {display: flex;justify-content: space-between;}.flex-row-ad {display: flex;justify-content: space-around;}.flex-row-end {display: flex;justify-content: flex-end;}.flex-wrap {flex-wrap: wrap;} /* 换行,默认nowrap(不换行)*/.flex-col {display: flex;flex-direction: column;}.flex-col-vcenter {display: flex;flex-direction: column;justify-content: center;} /* 纵向布局垂直居中 */.flex-col-rcenter {display: flex;flex-direction: column;align-items: center;} /* 纵向布局水平居中对齐 */.flex-col-c {display: flex;flex-direction: column;align-items: center;justify-content: center;}.flex-col-bw {display: flex;flex-direction: column;justify-content: space-between;}.flex1-row {flex: 1;width: 1px;}.flex1-col {flex: 1;height: 1px;}.w100 {width: 100%;}.h100 {height: 100%;}
5.在 main.js 中引入公共样式文件base.css和global.scss:
// ...// 引入 自定义 element-ui 主题样式import '../themes/index.css'//引入全局样式base.cssimport '@/assets/style/base.scss';//全局样式// 引入全局样式global.scssimport '@/common/style/global.scss'// ...
五、项目配置
5.1 eslint 配置
ESLint最初是由Nicholas C. Zakas 于2013年6月创建的开源项目。它的目标是提供一个插件化的javascript代码检测工具。
安装依赖:
yarn add eslint-plugin-prettier -D
移除 package.json 中 eslintConfig 配置项,在项目根目录 新建 .eslintrc.js 文件(用来配置eslint 检测规则):
// .eslintrc.jsmodule.exports = {root: true, // 标识根目录env: {node: true,browser: true},ignorePatterns: ['node_modules/'], // 需要忽略的特定文件和目录extends: [/*** @vue/cli-plugin-eslint 提供了三种模式的规则:* 必要(默认)规则:plugin:vue/essential* 强烈推荐规则: plugin:vue/strongly-recommended* 推荐规则: plugin:vue/recommended* 三者并不是独立的,而是包含关系:推荐规则 》强烈推荐规则 》必要(默认)规则* 此外,我们也可以在 rules 对这些规则做自己的设定。* 更多设置规则:https://eslint.vuejs.org/rules/*/// 'plugin:vue/recommended', // 推荐规则'plugin:vue/strongly-recommended' // 强烈推荐规则// 'plugin:vue/essential', // 必要(默认)规则],plugins: ['prettier'],parserOptions: {parser: '@babel/eslint-parser'},rules: {/*** 0 = off, 1 = warn, 2 = error*/'vue/html-self-closing': 0, // 关闭强制自闭合标签检测'vue/max-attributes-per-line': ['error', {'singleline': {'max': 6 // 单行最多6个属性},'multiline': {'max': 1 // 超过6个属性,多行显示,每行1个}}],'vue/singleline-html-element-content-newline': 0, // 关闭强制在单行元素的内容之前和之后使用换行符'no-tabs': ['error'], // 禁止使用tab缩进'indent': [2, 2], // 缩进风格,两个空格'semi': [0], // 关闭语句强制分号结尾'no-alert': 1, // 禁止使用 alert confirm prompt'no-dupe-keys': 'error', // 在创建对象字面量时不允许key重复 {a: 1, a: 1}'no-eval': 'error', // 禁止使用 eval'no-eq-null': 'error', // 禁止对 null 使用 == 或 != 运算符'no-func-assign': 'error', // 禁止重复的函数声明'no-implicit-coercion': 'warn', // 禁止隐式转换'no-mixed-spaces-and-tabs': [2, false], // 禁止混用 tab 和空格'no-multiple-empty-lines': [1, { max: 1 }], // 空行最多不能超过1行'no-redeclare': 2, // 禁止重复声明变量'no-spaced-func': 2, // 函数调用时 函数名与()之间不能有空格'no-trailing-spaces': 1, // 禁止行末空格'no-undef': 'warn', // 不能有未定义的变量'no-use-before-define': 2, // 未定义前不能使用 ?禁止声明提前'no-var': 2, // 禁止 var 声明变量,使用 let 和 const'comma-style': [2, 'last'], // 逗号风格,换行时在行首还是行尾'consistent-return': 0, // return 后面是否允许省略'consistent-this': [2, 'me'], // this 别名'default-case': 2, // switch 语句最后必须有 default'dot-location': 0, // 对象访问符的位置,换行时在行首还是行尾'id-length': [2, { min: 3 }], // 变量名长度'init-declarations': 0, // 声明时必须赋值'key-spacing': [2, { beforeColon: false, afterColon: true }], // 对象字面量中冒号的前后空格'lines-around-comment': 0, // 行前/行后注释'max-depth': [1, 4], // 嵌套块深度'space-before-function-paren': [1, 'always'], // 函数定义时括号前面要不要有空格'space-unary-ops': [0, { words: true, nonwords: false }], // 一元运算符的前/后要不要加空格'use-isnan': 2, // 禁止比较时使用NaN,只能用 isNaN()'vars-on-top': 2, // var必须放在作用域顶部'vue/require-v-for-key': 'warn', // vue v-for 必须绑定 key'no-await-in-loop': 'error', // 禁止在循环中使用 await'no-console': 'off', // 关闭禁止使用console'no-template-curly-in-string': 'error', // 禁用类似ES6模板字符串的字面量字符串定义'block-scoped-var': 'error', // 变量在定义块的外部使用时,规则会报错'complexity': ['error', { max: 10 }],'one-var': ['error', 'consecutive'], // 连续的变量声明只使用一个let或const关键字'quote-props': [ // 对象字面值属性名称可以用两种方式定义:使用文字或使用字符串'error','consistent', // 强制执行一致的引用风格需要引用对象字面值属性名称{keywords: true, // 关键字作为属性名称时必须加引号numbers: true // 数字作为属性名称时必须加引号}],'quotes': ['error', 'single'], // 字符串字面量强制使用单引号'no-new': 'off', // 关闭禁止使用new构造方法而不赋值'curly': ['error', 'all'], // 必须使用 if(){} 中的{}'eqeqeq': ['error', 'smart'], // 必须使用全等'no-empty-function': 'warn', // 禁止出现空函数'no-implied-eval': 'error', // 禁止使用 eval()'no-lone-blocks': 'error', // 禁用不必要的嵌套块'no-loop-func': 'error', //'no-multi-spaces': 'error', // 不能用多余的空格'no-multi-str': 'error', // 字符串不能用\换行'no-plusplus': 'off', // 关闭禁止 ++ --'no-return-assign': 'error', // return 语句中不能有赋值表达式'no-self-compare': 'error', // 不能比较自身'no-throw-literal': 'error', // 禁止抛出字面量错误 throw "error"'no-unmodified-loop-condition': 'error', // 循环中的变量经常在循环中修改。如果不是,那可能是一个错误。'no-useless-concat': 'error', // 没有必要将两个字符串连接在一起 var foo = "a" + "b";'no-delete-var': 'error', // 不能对var声明的变量使用delete操作符'comma-dangle': 'error', // 对象字面量项尾必须有逗号'comma-spacing': 'error', // 逗号前后的空格'computed-property-spacing': 'error', // 不允许计算属性括号内的空格'implicit-arrow-linebreak': 'error', // 在箭头函数体之前不允许换行'keyword-spacing': 'error', // 关键字前后至少有一个空格'semi-spacing': 'error', // 分号前后禁用空格'semi-style': 'error' // 强制分号位于语句的末尾}}
了解更多规则配置请移步:
👉 https://eslint.vuejs.org/rules/
👉 List of available rules - ESLint中文文档 (bootcss.com)
! 本文档中 eslint 检测规则配置只做说明举例,如果与公司骨架项目冲突的话,以骨架项目中为规范。 !
此时的 package.json :
{"name": "hello-world","version": "0.1.0","private": true,"scripts": {"serve": "vue-cli-service serve","build": "vue-cli-service build","lint": "vue-cli-service lint"},"dependencies": {"axios": "^0.26.1","core-js": "^3.8.3","element-ui": "^2.15.6","vue": "^2.6.14","vue-router": "3.5.3","vuex": "3.6.2"},"devDependencies": {"@babel/core": "^7.12.16","@babel/eslint-parser": "^7.12.16","@babel/preset-env": "^7.16.11","@vue/cli-plugin-babel": "~5.0.0","@vue/cli-plugin-eslint": "~5.0.0","@vue/cli-service": "~5.0.0","babel-plugin-component": "^1.1.1","eslint": "^7.32.0","eslint-plugin-prettier": "^4.0.0","eslint-plugin-vue": "^8.0.3","sass": "^1.49.9","sass-loader": "10.2.1","vue-template-compiler": "^2.6.14"},"browserslist": ["> 1%","last 2 versions","not dead"]}
HBuilder X 中配置 eslint
HBuilderX 2.6.8+版本起,新增eslint 实时校验、自动修复错误的功能。注意:此文不适用于2.6.8之前的版本
参考:HBuilderX 使用eslint实时校验、自动修复代码错误(适用于HBuilderX 2.6.8+) - DCloud问答
下载 eslint-js 插件 eslint-js插件下载地址
下载 eslint-vue 插件 eslint-vue插件下载地址
- eslint 文件保存,实时校验、自动修复错误功能说明
- 使用此功能,必须安装
eslint-js和eslint-vue插件。(菜单【工具】【插件安装】) vue-cli项目,需要安装eslint库,并配置eslint规则.- 若满足上述条件,当编写完代码,保存时,若代码中存在错误,自动修复;
- 使用此功能,必须安装
- 插件设置
2.6.11版本,支持自定义配置:
保存时自动修复和启用实时校验;见下图
特别说明: 实时校验功能,默认未开启,需要手动开启此功能
工具 👉设置👉插件配置 勾选 eslint-js 下的“启用实时校验” 和 eslint-vue 下的 “启用实时校验”
编码过程中 HBuider X 会自动按照 eslint 配置规则修复不规范的编码。
在项目根目录新建 .prettierrc.js文件:

module.exports = {printWidth: 120, //限制每行字符个数tabWidth: 4, //指定每个缩进级别的空格数useTabs: false, //使用制表符而不是空格缩进semi: false, //在语句末尾打印分号singleQuote: true, //使用单引号而不是双引号trailingComma: 'es5', // 在对象或数组最后一个元素后面是否加逗号(在ES5中加尾逗号)bracketSpacing: true, //在对象文字中的括号之间打印空格arrowParens: 'always', //始终给箭头函数的参数加括号htmlWhitespaceSensitivity: 'css', //指定HTML文件的全局空格敏感度endOfLine: 'auto' //检测换行符类型,如果出现大量换行符报错,可以修改为auto不检测}
HBuilder X 安装 prettier 插件
下载 prettier 插件 prettier插件下载地址
修改 HBuilder X 配置:
HBuilder X 编辑器格式化时, 使用的缩进方式,是读取的菜单【工具 —> 设置】中的配置。配置方式:点击“工具-插件安装-已安装的插件-prettier-配置”

vue 文件的开头在 HBuilder X 中有一个红色波浪线。解决方法:.eslintrc.js添加 requireConfigFile.false 的配置:
module.exports = {/// ...parserOptions: {parser: '@babel/eslint-parser',requireConfigFile: false // 解决 HBuilder X 中 vue 文件开头显示红色波浪线},/// ...}
5.2 element-ui 主题配置
Element 默认提供一套主题,CSS 命名采用 BEM 的风格,方便使用者覆盖样式。
5.2.1 方式一、自定义element-variables.scss文件方式修改主题【推荐】
1.Element 的 theme-chalk 使用 SCSS 编写,可以直接在项目中改变 Element 在项目common目录创建 element-variables.scss,写入以下内容:
/* 改变主题色变量 */$--color-primary: teal;/* 改变 icon 字体路径变量,必需 */$--font-path: '~element-ui/lib/theme-chalk/fonts';@import "~element-ui/packages/theme-chalk/src/index";
2.入口文件中引入:
import Vue from 'vue'import Element from 'element-ui'import './element-variables.scss'Vue.use(Element)
需要注意的是,覆盖字体路径变量是必需的,将其赋值为 Element 中 icon 图标所在的相对路径即可。
5.2.2 方式二、深层次的主题定制
1.安装主题生成工具
yarn add element-themex -D
2.安装白垩主题
yarn add element-theme-chalk -D
3.初始化变量文件
主题生成工具安装成功后,通过 node_modules/.bin/et 访问到命令。执行 -i 初始化变量文件。默认输出到 element-variables.scss
./node_modules/.bin/et -i
当前目录会有一个 element-variables.scss 文件。内部包含了主题所用到的所有变量,它们使用 SCSS 的格式定义。大致结构如下:
$--color-primary: #409EFF !default;$--color-primary-light-1: mix($--color-white, $--color-primary, 10%) !default; /* 53a8ff */$--color-primary-light-2: mix($--color-white, $--color-primary, 20%) !default; /* 66b1ff */$--color-primary-light-3: mix($--color-white, $--color-primary, 30%) !default; /* 79bbff */$--color-primary-light-4: mix($--color-white, $--color-primary, 40%) !default; /* 8cc5ff */$--color-primary-light-5: mix($--color-white, $--color-primary, 50%) !default; /* a0cfff */$--color-primary-light-6: mix($--color-white, $--color-primary, 60%) !default; /* b3d8ff */$--color-primary-light-7: mix($--color-white, $--color-primary, 70%) !default; /* c6e2ff */$--color-primary-light-8: mix($--color-white, $--color-primary, 80%) !default; /* d9ecff */$--color-primary-light-9: mix($--color-white, $--color-primary, 90%) !default; /* ecf5ff */$--color-success: #67c23a !default;$--color-warning: #e6a23c !default;$--color-danger: #f56c6c !default;$--color-info: #909399 !default;...
直接编辑 element-variables.scss 文件,例如修改主题色为红色。
$--color-primary: red;
4.编译主题
在 package.json 中 scripts 下添加编译主题指令:
"build:theme": "node_modules/.bin/et -o ./themes",
此时 package.json :
{"name": "hello-world","version": "0.1.0","private": true,"scripts": {"serve": "vue-cli-service serve","build": "vue-cli-service build","build:theme": "node_modules/.bin/et -o ./themes","lint": "vue-cli-service lint"},"dependencies": {"axios": "^0.26.1","core-js": "^3.8.3","element-ui": "^2.15.6","vue": "^2.6.14","vue-router": "3.5.3","vuex": "3.6.2"},"devDependencies": {"@babel/core": "^7.12.16","@babel/eslint-parser": "^7.12.16","@babel/preset-env": "^7.16.11","@vue/cli-plugin-babel": "~5.0.0","@vue/cli-plugin-eslint": "~5.0.0","@vue/cli-service": "~5.0.0","babel-plugin-component": "^1.1.1","element-theme-chalk": "^2.15.6","element-themex": "^1.0.3","eslint": "^7.32.0","eslint-plugin-prettier": "^4.0.0","eslint-plugin-vue": "^8.0.3","sass": "^1.49.9","sass-loader": "10.2.1","vue-template-compiler": "^2.6.14"},"browserslist": ["> 1%","last 2 versions","not dead"]}
执行 yarn build:theme 会在根目录生成 themes 文件夹
5.引入自定义主题
修改main.js 中默认主题引入为
// 引入 element-ui 样式// import 'element-ui/lib/theme-chalk/index.css'// 引入 自定义 element-ui 主题样式import '../themes/index.css'
运行 yarn serve 查看效果。


六、目录结构说明
6.1 项目目录结构
├─dist 生产环境打包代码输出目录 ├─public 静态资源文件目录 │ └─index.html 接口地址目录 ├─src 源码目录 │ ├─api 接口地址目录 │ ├─assets 静态资源文件目录 │ ├─common 公共文件 │ │ └─style 公共样式相关文件 │ │ ├─base.css全局公共样式 │ │ ├─var.scss全局公共样式 │ │ └─global.scss 公共样式 scss 变量 │ │ └─mixins.scss 公共样式 scss 变量 │ │ └─element-variables.scss 重置elementui主题样式 scss 变量 │ ├─components 组件目录 │ ├─router 路由文件目录 │ │ └─index.js 路由主文件 │ ├─store vuex文件目录 │ │ ├─modules vuex 模块 │ │ └─index vuex 主文件 │ ├─utils 工具目录 │ │ ├─element.js element-ui 组件按需引入 │ │ ├─utils.js 工具方法 │ │ └─request 封装的 axios 实例方法 │ ├─views 页面目录 │ ├─App.vue 入口 vue 文件 │ ├─main.js 入口 js 文件 ├─babel.config.js babel 配置文件 ├─jsconfig.json js 编译 配置文件 ├─package.json └─vue.config.js vue/cli 配置文件
七、集成echarts
1.安装依赖:
yarn add echarts@4.9.0
2.在main.js中全局引入 echarts:
/// ...// 引入 echartsimport echarts from 'echarts'Vue.prototype.$echarts = echarts/// ...
3.在components中创建echarts/echart-temp.vue文件,作为 echarts 公用组件:
<template><div class="chart-container" ref="container"><div class="chart-body" ref="chartDiv" :style="style"></div></div></template><script>// 引入防抖函数import { debounce } from '@/utils'export default {name: 'EchartTemp',props: {options: {type: Object,default () {return null}},styles: {type: Object,default () {return {}}}},data () {return {style: {},chartObj: null,chartData: {},chartDom: null,// eslint-disable-next-line vue/no-reserved-keys__resizeHanlder: null}},watch: {styles (val, oldVal) {this.style = val},options () {this.drawChart()}},mounted () {// alert('mounted!');// console.log(11111,this.options)this.chartDom = this.$refs.chartDivthis.chartObj = this.$echarts.init(this.chartDom)this.chartObj.on('click', (params) => {this.itemClick(params)})this.chartObj.on('legendselectchanged', (params) => {console.log('this.chartObj: ', this.chartObj)this.legendClick(this.chartObj, params)})this.drawChart()this.__resizeHanlder = debounce(this.refreshChart)// 添加尺寸改变事件window.addEventListener('resize', this.__resizeHanlder)// this.$bus.$on('toggleSideMenu', this.__resizeHanlder)},activated () {// alert('activated!');// 添加尺寸改变事件// window.addEventListener('resize', this.__resizeHanlder);// this.$bus.$on('toggleSideMenu', this.__resizeHanlder);this.resizeChart()},computed: {},methods: {// 刷新图表refresh () {this.drawChart()},clearChart () {// this.chartDom.innerHTML = ''this.chartObj.clear()},resizeChart () {let totleHeight = this.$refs['container'].clientHeight// this.styles = { height: totleHeight + 'px' };this.style = { height: totleHeight + 'px' }this.refreshChart()},refreshChart () {// console.log("this.chartObj",this.chartObj.resize);this.chartObj.resize()},drawChart () {this.clearChart()if (this.options && Object.keys(this.options).length > 0) {this.chartObj.setOption(this.options)}},setOption (option) {this.chartObj.setOption(option)this.drawChart()},itemClick (params) {this.$emit('itemClick', params)},legendClick (chartObj, params) {this.$emit('legendClick', chartObj, params)},setProps (props) {if (Object.keys(props).length > 0) {for (let key in props) {this[key] = props[key]}this.$nextTick(() => {this.resizeChart()this.drawChart()})}}},beforeDestroy () {// alert('beforeDestroy');window.removeEventListener('resize', this.refreshChart)// this.$bus.$off('toggleSideMenu', this.__resizeHanlder)},components: {}}</script><style lang="scss" scoped>.chart-container {width: 100%;height: 100%;display: flex;flex-direction: column;.chart-body {flex: 1;}}</style>
src/utils/index.js:
// 防抖函数export function debounce (func, delay) {// 维护一个 timerlet timer = null;return function () {clearTimeout(timer);// eslint-disable-next-line consistent-thisconst context = this,args = arguments;timer = setTimeout(function () {func.apply(context, args);}, delay);};}
组件使用:
<template><div class="echart-demo"><echart-temp :options="options"></echart-temp></div></template><script>import EchartTemp from '@/components/echarts/echart-temp.vue'export default {name: 'EchartDemo',components: {EchartTemp},data () {return {options: {}}},mounted () {this.initData()},methods: {initData () {let xAxisData = [],data1 = [],data2 = []// eslint-disable-next-line id-lengthfor (let i = 0; i < 100; i++) {xAxisData.push('类目' + i)data1.push((Math.sin(i / 5) * (i / 5 - 10) + i / 6) * 5)data2.push((Math.cos(i / 5) * (i / 5 - 10) + i / 6) * 5)}this.drawChart(xAxisData, data1, data2)},drawChart (xAxisData, data1, data2) {this.options = {title: {text: '柱状图动画延迟'},legend: {data: ['bar', 'bar2']},toolbox: {// y: 'bottom',feature: {magicType: {type: ['stack', 'tiled']},dataView: {},saveAsImage: {pixelRatio: 2}}},tooltip: {},xAxis: {data: xAxisData,splitLine: {show: false}},yAxis: {},series: [{name: 'bar',type: 'bar',data: data1,animationDelay: function (idx) {return idx * 10}},{name: 'bar2',type: 'bar',data: data2,animationDelay: function (idx) {return idx * 10 + 100}}],animationEasing: 'elasticOut',animationDelayUpdate: function (idx) {return idx * 5}}}}}</script><style lang="scss" scoped>.echart-demo {width: 800px;height: 600px;border: 1px solid darkblue;}</style>
效果:

八、常见问题
8.1 vue-router 不同的历史模式
在创建路由器实例时,history 配置允许我们在不同的历史模式中进行选择。
- Hash 模式#
hash 模式是用 createWebHashHistory() 创建的:
import { createRouter, createWebHashHistory } from 'vue-router'const router = createRouter({history: createWebHashHistory(),routes: [//...],})
它在内部传递的实际 URL 之前使用了一个哈希字符(#)。由于这部分 URL 从未被发送到服务器,所以它不需要在服务器层面上进行任何特殊处理。不过,它在 SEO 中确实有不好的影响。如果你担心这个问题,可以使用 HTML5 模式。
- HTML5 模式#
用 createWebHistory() 创建 HTML5 模式,推荐使用这个模式:
import { createRouter, createWebHistory } from 'vue-router'const router = createRouter({history: createWebHistory(),routes: [//...],})
当使用这种历史模式时,URL 会看起来很 “正常”,例如 https://example.com/user/id。漂亮!
不过,问题来了。由于我们的应用是一个单页的客户端应用,如果没有适当的服务器配置,用户在浏览器中直接访问 https://example.com/user/id,就会得到一个 404 错误。这就丑了。
不用担心:要解决这个问题,你需要做的就是在你的服务器上添加一个简单的回退路由。如果 URL 不匹配任何静态资源,它应提供与你的应用程序中的 index.html 相同的页面。漂亮依旧!
8.2 cnpm与nrm
使用 npm安装包时是从国网服务器下载,受网络影响大,会遇到时间长,甚至安装失败的问题。此时可以选择使用国内镜像源进行安装,例如国内的淘宝镜像等来代替npm或者yarn的官方服务器。
常用的是使用官方推荐的 cnpm 命令行工具代替默认的 npm,实际使用的是淘宝源:
npm install -g cnpm --registry=https://registry.npm.taobao.org
注意:有时使用 cnpm 安装的路径可能回存在问题,在使用react-native开发应用时会出现问题。此时可以使用nrm切换淘宝源。
8.2.1nrm
nrm是一个npm源管理器,可以快速的切换npm源,使用nrm有两种方式
- npm安装nrm
npm install nrm -g
- 使用 nrm
所以使用nrm切换镜像源可以是:
# 查看当前可用的源nrm ls# 切换源nrm use taobao# 添加源nrm add '镜像名称' '镜像地址'# 删除源nrm del '镜像名称'
8.3 node 版本管理工具 nvm
nvm是一个node的版本管理工具,可以简单操作node版本的切换、安装、查看等等
常用命令:
# 列出本地已安装的node版本nvm ls# 安装node命令nvm install '版本号'# 卸载node命令nvm uninstall '版本号'# 切换 node 版本号nvm use '版本号'# 查看当前版本nvm current
8.4 yarn 环境安装依赖报错解决方法(info fsevents@1.2.7)
yarn config set ignore-engines true
8.5 解决自定义element-ui主题,刷新页面偶尔出现的字体图标乱码
- vue.config.js 增加
sass自定义配置
'use strict'module.exports = {css: {loaderOptions: {sass: {sassOptions: {// 生效代码:compressed/nested/expanded/compactoutputStyle: 'expanded'}}}}}
- 升级
sass到1.54.x版本。

