加快Vue项目的开发速度

现如今的开发,比如是内部使用的管理平台这种项目大都时间比较仓仓促。实际上来说在使用了webpack + vue 这一套来开发的话已经大大了提高了效率。但是对于我们的开发层面。还是有很多地方可以再次提高我们的项目开发效率,让我们更加专注于业务,毕竟时间就是生命。下面我们挨个来探讨。

巧用Webpack

Webpack 是实现我们前端项目工程化的基础,但其实她的用处远不仅仅如此,我们可以通过 Webpack 来帮我们做一些自动化的事情。首先我们要了解 require.context() 这个API

require.context()

您可以使用[require.context](Dependency Management | webpack)()函数创建自己的上下文。 它允许您传入一个目录进行搜索,一个标志指示是否应该搜索子目录,还有一个正则表达式来匹配文件。

其实是 Webpack 通过解析 require() 的调用,提取出来如下这些信息:

Directory: ./template
Regular expression: /^.*\.ejs$/

然后来创建我们自己的上下文,什么意思呢,就是我们可以通过这个方法筛选出来我们需要的文件并且读取

下面我们来简单看一看使用:

/*** @param directory 要搜索的文件夹目录不能是变量,否则在编译阶段无法定位目录
* @param useSubdirectories  是否搜索子目录
* @param regExp 匹配文件的正则表达式
* @return function 返回一个具有 resolve, keys, id 三个属性的方法resolve() 它返回请求被解析后得到的模块 idkeys() 它返回一个数组,由所有符合上下文模块处理的请求组成。 id 是上下文模块里面所包含的模块 id. 它可能在你使用 module.hot.accept 的时候被用到*/
require.context('demo', useSubdirectories = false, regExp = /\.js$/)
// (创建了)一个包含了 demo 文件夹(不包含子目录)下面的、所有文件名以 `js` 结尾的、能被 require 请求到的文件的上下文。

不要困惑,接下来我们来探讨在项目中怎么用。

组织路由

对于 Vue 中的路由,大家都很熟悉,类似于声明式的配置文件,其实已经很简洁了。现在我们来让他更简洁

  1. 分割路由

首先为了方便我们管理,我们把router目录下的文件分割为以下结构

router                           // 路由文件夹|__index.js                    // 路由组织器:用来初始化路由等等|__common.js                   // 通用路由:声明通用路由|__modules                     // 业务逻辑模块:所以的业务逻辑模块|__index.js              // 自动化处理文件:自动引入路由的核心文件|__home.js               // 业务模块home:业务模块|__a.js                  // 业务模块a
  1. modules 文件夹中处理业务模块

modules 文件夹中存放着我们所有的业务逻辑模块,至于业务逻辑模块怎么分,我相信大家自然有自己的一套标准。我们通过上面提到的 require.context() 接下来编写自动化的核心部分 index.js

const files = require.context('.', true, /\.js$/)console.log(files.keys()) // ["./home.js"] 返回一个数组
let configRouters = []
/*** inject routers*/
files.keys().forEach(key => {if (key === './index.js') returnconfigRouters = configRouters.concat(files(key).default) // 读取出文件中的default模块
})
export default configRouters // 抛出一个Vue-router期待的结构的数组

自动化部分写完了,那业务组件部分怎么写? 这就更简单了

import Frame from '@/views/frame/Frame'
import Home from '@/views/index/index'
export default [// 首页{path: '/index',name: '首页',redirect: '/index',component: Frame, children: [ // 嵌套路由{path: '',component: Home}]}
]
  1. common 路由处理 我们的项目中有一大堆的公共路由需要处理比如 404 阿,503 阿等等路由我们都在 common.js 中进行处理。
export default [// 默认页面{path: '/',redirect: '/index',hidden:true},// 无权限页面{path: '/nopermission',name: 'nopermission',component: () => import('@/views/NoPermission')},// 404{path: '*',name: 'lost',component: () => import('@/views/404')}
]
  1. 路由初始化 这是我们的最后一步了,用来初始化我们的项目路由
import Vue from 'vue'
import VueRouter from 'vue-router'
import RouterConfig from './modules' // 引入业务逻辑模块
import CommonRouters from './common' // 引入通用模块
Vue.use(VueRouter)
export default new VueRouter({mode: 'history',// 需要服务端支持scrollBehavior: () => ({ y: 0 }),routes: RouterConfig.concat(CommonRouters)
})

估计有些朋友代码写到这还不知道到底这样做好处在哪里。我们来描述一个场景,比如按照这种结构来划分模块。正常的情况是我们创建完 home.js 要手动的把这个模块 import 到路由文件声明的地方去使用。但是有了上面的 index.js ,在使用的时候你只需要去创建一个 home.js 并抛出一个符合 VueRouter 规范的数组,剩下的就不用管了。import RouterConfig from './modules' // 引入业务逻辑模块 已经帮你处理完了。另外扩展的话你还可以把 hooks 拿出来作为一个单独文件。

全局组件统一声明

同样的道理,有了上面的经验,我们照葫芦画瓢来处理一下我们的全局组件。这就没什么可说的了,直接上核心代码

  1. 组织结构
components                       // 组件文件夹|__xxx.vue                     // 其他组件|__global                      // 全局组件文件夹|__index.js              // 自动化处理文件|__demo.vue              // 全局demo组件
  1. global 处理
import Vue from 'vue'
let contexts = require.context('.', false, /\.vue$/)
contexts.keys().forEach(component => {let componentEntity = contexts(component).default// 使用内置的组件名称 进行全局组件注册Vue.component(componentEntity.name, componentEntity)
})
  1. 使用和说明

这个使用起来就更简单了,直接在 app.js 引用这个文件就行。

注意:我之前看到有些人做法是使用组件名去区分全局组件和普通组件,然后通过正则去判断需不需要全局注册。我是直接把全局的组件放到 global 文件夹下,然后组件的注册名称直接使用component.name 。至于使用哪种方式就比较看个人了。

充分利用NodeJS

放着 node 这么好得东西不用真是有点浪费,那么我们来看看 node 能为我们增加效率做出什么贡献。

有这么一个场景,我们每次创建模块的时候都要新建一个 vue 文件和对应的 router 配置,而且新页面的大部分东西都还差不多,还得去复制粘贴别得页面。这想想就有点 low 。那既然有了 node 我们可不可以通过 node 来做这写乱七八糟得事情? 下面来把我们的想法付诸于显示。

我们实现这个功能主要要借助 Node 的 [fs](fs 文件系统 | Node.js API 文档 (nodejs.cn)) 和 [process](process 进程 | Node.js API 文档 (nodejs.cn)), 感兴趣的话可以深入研究一下。

首先我们要编写我们的 node 脚本,这里是一个比较简单的版本。什么验证文件夹或者文件的都没有,只是来实现我们这个想法:

/** fast add new module script*/
const path = require('path')
const fs = require('fs')
const chalk = require('chalk')
const reslove = file => path.resolve(__dirname, '../src', file)
// symbol const
const RouterSymbol = Symbol('router'),ViewsSymbol = Symbol('views')
// root path
const rootPath = {[RouterSymbol]: reslove('router/modules'),[ViewsSymbol]: reslove('views')
}
//loggs
const errorLog = error => console.log(chalk.red(`${error}`))
const defaultLog = log => console.log(chalk.green(`${log}`))
// module name
let moduleName = new String()
let fileType = new String()
//const string
const vueFile = module => (`<template></template><script>
export default {name: '${module}',data () {return {}},methods: {},created() {}
}
</script><style lang="less"></style>
`)
// route file
const routerFile = module => (`// write your comment here...
export default [{path: '/${module}',name: '',redirect: '/${module}',component: () => import('@/views/frame/Frame'),children: [{path: '',fullPath: '',name: '',component: () => import('@/views/${module}/index')}]}
]
`)
/*** generate file* @param {*} filePath * @param {*} content * @param {*} dirPath */
const generateFile = async (filePath, content, dirPath = '') =>{try {// create file if file not exitif (dirPath !== '' && ! await fs.existsSync(dirPath)) {await fs.mkdirSync(dirPath)defaultLog(`created ${dirPath}`)}if (! await fs.existsSync(filePath)) {// create fileawait fs.openSync(filePath, 'w')defaultLog(`created ${filePath}`)}await fs.writeFileSync(filePath, content, 'utf8')} catch (error) {errorLog(error)}
}
// module-method map
const generates = new Map([['view', async (module) => {// module fileconst filePath = path.join(rootPath[ViewsSymbol], module)const vuePath = path.join(filePath, '/index.vue')await generateFile(vuePath, vueFile(module), filePath)}],// router is not need new folder['router',async (module) => {const routerPath = path.join(rootPath[RouterSymbol], `/${module}.js`)await generateFile(routerPath, routerFile(module))}]
])
defaultLog(`请输入模块名称(英文):`)
// files
const files = ['view', 'router']
// 和命令行进行交互 获取的创建的模块名称
process.stdin.on('data', (chunk) => {try {if (!moduleName) {moduleName = chunk} else {chunk = chunk.slice(0,-2) // delete /ndefaultLog(`new module name is ${chunk}`)files.forEach(async (el, index) => {// 执行创建语句await generates.get(`${el}`).call(null, chunk.toString())if (index === files.length-1) {process.stdin.emit('end')}})}} catch (error) {errorLog(error)}
})
process.stdin.on('end', () => {defaultLog('create module success')
})

下面我们看使用的流程

这样我们就分别创建了vue和router的文件,而且已经注入了内容。按照我们提前声明的组件

注意:这只是一个简单的思路,通过Node强大的文件处理能力,我们能做的事情远不止这些。

发挥Mixins的威力

Vue 中的 混入 [mixins](混入 — Vue.js (vuejs.org)) 是一种提供分发 Vue 组件中可复用功能的非常灵活的方式。听说在3.0版本中可能会用Hooks的形式实现,但这并不妨碍它的强大。基础部分的可以看[这里](CodeBlog/Vue开发中问题集锦.md at master · QDMarkMan/CodeBlog (github.com))。这里主要来讨论mixins能在什么情景下帮助我们。

通用mixins

如果我们有大量的表格页面,仔细一扒拉你发现非常多的东西都是可以复用的例如 分页表格高度加载方法laoding声明 等一大堆的东西。下面我们来整理出来一个简单通用混入list.js

const list = {data () {return {// 这些东西我们在list中处理,就不需要在每个页面再去手动的做这个了。loading: false, // 伴随loading状态pageNo: 1, // 页码pageSize: 15, // 页长totalCount: 0, // 总个数pageSizes: [15, 20, 25, 30], //页长数pageLayout: 'total, sizes, prev, pager, next, jumper', // 分页布局list: []}},methods: {// 分页回掉事件handleSizeChange(val) {this.pageSize = val// todo},handleCurrentChange (val) {this.pageNo = val// todo},/*** 表格数据请求成功的回调 处理完公共的部分(分页,loading取消)之后把控制权交给页面* @param {*} apiResult * @returns {*} promise*/listSuccessCb (apiResult = {}) {return new Promise((reslove, reject) => {let tempList = [] // 临时listtry {this.loading = false// todo// 直接抛出reslove(tempList)} catch (error) {reject(error)}})},/*** 处理异常情况* ==> 简单处理  仅仅是对表格处理为空以及取消loading*/listExceptionCb (error) {this.loading = falseconsole.error(error)}},created() {// 这个生命周期是在使用组件的生命周期之前this.$nextTick().then(() => {// todo})}
}
export default list

下面我们直接在组件中使用这个 mixins

import mixin from '@/mixins/list' // 引入
import {getList} from '@/api/demo'
export default {name: 'mixins-demo',mixins: [mixin], // 使用mixinsdata () {return {}},methods: {// 加载列表load () {const para = {}this.loading = truegetList(para).then((result) => {this.listSuccessCb(result).then((list) => {this.list = list}).catch((err) => {console.log(err)})}).catch((err) => {this.listExceptionCb(err)})}},created() {this.load()}
}

使用了 mixins 之后一个简单的有 loadoing , 分页 , 数据 的表格大概就只需要上面这些代码。

mixins做公共数据的管理

有些时候我们有一些公共的数据它可能3,4个模块取使用但是又达不到全局的这种规模。这个时候我们就可以用 mixins 去管理他们,比如我们有几个模块要使用用户类型这个列表,我们来看使用 mixins 来实现共享。

// types.js
import {getTypes} from '@/api/demo' // ajax
export default {data () {return {types: [] // ==>  {name: '', value: ''}}},methods: {// 获取列表getAllTypesList () {getApiList().then((result) => {// todothis.types = result // 假设result就是我们需要使用的数据}).catch((err) => {console.error(err)})}},created() {// 在需要使用这个mixins的时候取自动请求数据  这个可要可不要  你想在父组件中执行也是ok的this.getAllTypesList()}
}

在组件中引用

import typeMixin from '@/mixins/types'
export default {name: 'template',mixins: [typeMixin],data () {return {// types这个数组在使用组件中不用多余的定义,直接拿来用就行type: ''}},methods: {}
}

至于 mixins 中得数据我们可以在组件中直接使用

<!--  -->
<el-select v-model="type" clearable placeholder="请选择类型"><el-option v-for="item in types" :key="item.id" :label="item.templateName" :value="item.id"></el-option>
</el-select>

我们这样就可以不用 vuex 来去管理那些只有在模块间复用几次的数据,而且非常方便得去取我们想要得数据,连定义都省了。但是这有一个缺点。就是每次都会去重新请求这些数据。如果你不在乎这一点点瑕疵的话,我觉得用起来是完全ok得。

注意mixins 它固然是简单的,但是注释和引用一定要做好,不然的话新成员进入团队大概是一脸的懵逼,而且也不利于后期的维护。也是一把双刃剑。另外:全局 mixins 一定要慎用,如果不是必须要用的话我还是不建议使用。

进一步对组件进行封装

大家都知道组件化的最大的好处就是高度的可复用性和灵活性。但是组件怎么封装好,封装到什么程度让我们更方便。这是没有标准的答案的。我们只有根据 高内聚,低耦合 的这个指导思想来对我们的业务通用组件来进行封装,让我们的业务页面结构更加的简洁,加快我们的开发效率。封装多一点的话页面可能会变成这样:

<template><box-content><!-- 头部标题部分 --><page-title><bread slot="title" :crumbs="[{name: 'xx管理', path: '', active: true, icon: ''}, {name: 'xxxx', path: '', active: true, icon: ''}]"></bread></page-title><!-- 表格部分 --><div><base-table v-loading="loading" :columns="headers" :list="list" :page-no ="pageNo" :page-size="pageSize" :total-count="totalCount" @delete="deleteItm"  @change-size="handleSizeChange" @change-page="handleCurrentChange"></base-table></div></box-content>
</template>

有什么东西一目了然。

无状态组件

最容易勾起我们封装欲望的就是无状态 HTML 组件,例如我们除去 header, menu 之后的 content 部分。没有什么需要复杂的交互,但是我们每个页面又都得写。你说不拿它开刀拿谁开

加快Vue项目的开发速度相关推荐

  1. 用Docker搭建Laravel和Vue项目的开发环境

    在这篇文章中我们将通过Docker在个人本地电脑上构建一个快速.轻量级.不依赖本地电脑所安装的任何开发套件的可复制的Laravel和Vue项目的开发环境(开发环境的所有依赖都安装在Docker构建容器 ...

  2. Vue项目构建开发入门

    Vue项目构建开发入门 开篇:Vue CLI 3 项目构建基础 大家好,当你点进这个标题,开始阅读本章的时候,说明你对 Vue.js 是充满好奇心和求知欲的.我之前写过一篇文章,这样评价 Vue.js ...

  3. [vue] 分析下vue项目本地开发完成后部署到服务器后报404是什么原因呢?

    [vue] 分析下vue项目本地开发完成后部署到服务器后报404是什么原因呢? 1.检查nginx配置,是否正确设置了资源映射条件: 2.检查vue.config.js中是否配置了publicPath ...

  4. 浅谈vue项目进阶开发-杂谈1

    写这一篇完全是想把自己平时对vue的使用体验与理解看记录下来,如有错误欢迎指正. 1.router的beforeEach与afterEach钩子函数 在开发vue项目时遇到这样两个问题:第一个问题是用 ...

  5. Vue项目区分开发环境问题

    记录背景 在Jenkins持续构建项目时,出现的IP和Port变化的情况. 也考虑到后期会换底层数据库,Redis配置和上传下载路径,可能会随着服务器的不同配置多台不同的环境变量. 所以区分一下各自开 ...

  6. 使用 typescript ,提升 vue 项目的开发体验(1)

    此文已由作者张汉锐授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 前言:对于我们而言,typescript 更像一个工具 官方指南 从 vue2.5 之后,vue 对 ts ...

  7. Vue纯零基础教学第三天--到走入Vue项目实际开发的内心深处

    让我们手摸着手大步向前走 ,最近看完 曾国藩传,觉得很不错,还有杜月笙与孟小冬 ,推荐大家在写代码的时候,要多读书,好增加自己的文艺气息.多些对书的感悟,放心游戏,多出去走走,今天的北京 真冷 3月3 ...

  8. vue 项目前端开发流程

    一  前端开发流程 1 添加路由 文件的位置:E:\vue-sdgt\src\router\index.js 2 设置跳转页面路径 跳转页面的物理路径:E:\vue-sdgt\src\views\ta ...

  9. 关于VUE项目地图开发中大量点标记绘制一些总结

    问题说明 在地图开发中,当地图中绘制大量的标记点后,无论是拖动或者缩放,都会感觉到明显的卡顿现象.(一般超过800个点后就比较明显了).在平时的工作业务中,由于公司的实时监控页面需要展现5000-20 ...

最新文章

  1. python2.7抓取豆瓣电影top250
  2. 【模板】 最小生成树
  3. 缩减oracle日志,[20180829]减少日志生成量.txt
  4. 移动推送消息送达常见问题与解决办法
  5. wordpress友联_Wordpress 友情链接页面终极版 – Fatesinger
  6. 2019.8.1正则二
  7. 用函数调用的方法输出乘法口诀表
  8. 数据库 SQL Server2012安装步骤详解
  9. 最新PYTHON批量下载快手个人主页短视频代码
  10. linux备份目录命令tar,Linux中使用tar命令备份与还原数据
  11. php微信消息通知,企业微信实现消息通知功能
  12. 正态分布是离散分布还是连续分布_正态分布,谁与争峰
  13. 计算机电源故障引起火灾,计算机硬件的常见故障及维护方法
  14. 20210412SQL实现全称量词和集合查询
  15. 京东商智-指数转换/指数还原
  16. 微信小程序实现vtt视频字幕
  17. 华为鸿蒙与小米新系统,华为鸿蒙系统硬刚谷歌!小米也发布新系统:却选择和谷歌系统互补...
  18. 支持DoH的DNS服务器,谷歌公共DNS正式支持DoH加密 更安全并且不影响速度
  19. MySQL 中 不等于 会过滤掉 Null 的问题
  20. Perl-字符串与排序2

热门文章

  1. 前端代码获取文件大小_vue技术栈前端建设方案
  2. kotlin读取sd卡里的文件_如何在Kotlin中写入文件?
  3. was css,Sass无效的CSS…:期望的表达式(例如,1px,粗体),是“{”
  4. oracle查看表中记录数,Oracle 查询某一用户下所有表的记录数
  5. kewail node 短信_Node.js代码示例_Node.js短信短信 | 微米-中国领先的短信彩信接口平台服务商...
  6. 第一章信息安全基础考试要点及真题分布
  7. mysql关键字使用顺序_MySQL数据库之单表查询中关键字的执行顺序
  8. 操作类名-对象语法//操作类名-数组语法
  9. JS-面向对象-This的指向---简单的函数调用 / 作为对象的方法调用时 / 作为构造函数调用时
  10. BoostrapTable的refresh和refreshOptions区别