如何加快Vue项目的开发速度

本文摘自奇舞周刊,侵权删。

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

巧用Webpack

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

require.context()

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

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

Directory: ./template

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

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

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

/**

* @param directory 要搜索的文件夹目录不能是变量,否则在编译阶段无法定位目录

* @param useSubdirectories 是否搜索子目录

* @param regExp 匹配文件的正则表达式

* @return function 返回一个具有 resolve, keys, id 三个属性的方法

resolve() 它返回请求被解析后得到的模块 id

keys() 它返回一个数组,由所有符合上下文模块处理的请求组成。

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

2、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') return

configRouters = 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

}

]

}

]

3、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')

}

]

4、路由初始化这是我们的最后一步了,用来初始化我们的项目路由。

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组件

2、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)

})

3、使用和说明

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

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

充分利用NodeJS

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

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

我们实现这个功能主要要借助Node的fs和process, 感兴趣的话可以深入研究一下。

首先我们要编写我们的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 exit

if (dirPath !== '' && ! await fs.existsSync(dirPath)) {

await fs.mkdirSync(dirPath)

defaultLog(`created ${dirPath}`)

}

if (! await fs.existsSync(filePath)) {

// create file

await 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 file

const 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 /n

defaultLog(`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 组件中可复用功能的非常灵活的方式。听说在3.0版本中可能会用Hooks的形式实现,但这并不妨碍它的强大。这里主要来讨论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 = [] // 临时list

try {

this.loading = false

// todo

// 直接抛出

reslove(tempList)

} catch (error) {

reject(error)

}

})

},

/**

* 处理异常情况

* ==> 简单处理 仅仅是对表格处理为空以及取消loading

*/

listExceptionCb (error) {

this.loading = false

console.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], // 使用mixins

data () {

return {

}

},

methods: {

// 加载列表

load () {

const para = {

}

this.loading = true

getList(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) => {

// todo

this.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. 加快Vue项目的开发速度

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

  2. vue项目的开发

    vue项目的开发 我们已经通过命令行创建了一个vue项目,并且打开了这个项目.下面是这个文件的src文件夹,这个文件夹放了整个项目的核心代码. 一.vue文件的用处简介. 1.assets文件夹,用来 ...

  3. Intellij idea 12和设置快捷键修改(加快项目的开发速度与养成良好习惯)

    1.为了养成良好的代码习惯idead中的javascript jSLint能显示不良的代码设置如下    2.Intellij idea 12每一次修改,保存生成都要按ctrl+shift+F9非常影 ...

  4. Vue项目在开发环境跨域和生成环境部署跨域问题解决

    一.在dev环境下的跨域问题解决1.项目使用的是axios请求网络,将baseUrl修改为/api (这里是使用webpack提供的代理功能将/api代理成目标接口host) axios.defaul ...

  5. vue 项目的开发流程

    1.$ node -v (检测node版本,node版本需要在 V4 以上) 2.全局安装vue $ npm install -g vue 3.安装脚手架 $ npm install -g vue-c ...

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

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

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

    此文已由作者张汉锐授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. vuex-class 提供了和 vuex 相关的全部装饰器,从而解决了上面 Vue.extend + vue ...

  8. vue项目 首页开发 part2

    知识点回顾 git clone: 从远程服务器克隆一个一模一样的版本库到本地,复制的是整个版本库, 叫做clone.(clone是将一个库复制到你的本地,是一个本地从无到有的过程) 1. 创建分支 进 ...

  9. Vue项目的开发流程

    我先安装的node.js 1.确认已安装了node.js,可在cmd中输入( node -v和npm -v),如显示出版号,说明安装成功 2.安装webpack 和webpack-cli 在全局下安装 ...

随机推荐

  1. 【leetcode】1021. Remove Outermost Parentheses

    题目如下: A valid parentheses string is either empty (""), "(" + A + ")", ...

  2. 【leetcode】75. Sort Colors

    题目如下: 解题思路:我的解题思路是遍历数组,遇到0删除该元素并插入到数组头部,遇到1则不处理,遇到2删除该元素并插入到数组尾部. 代码如下: class Solution(object): def ...

  3. 【leetcode】416. Partition Equal Subset Sum

    题目如下: 解题思路:对于这种判断是否的题目,首先看看动态规划能不能解决.本题可以看成是从nums中任选i个元素,判断其和是否为sum(nums)/2,很显然从nums中任选i个元素的和的取值范围是[ ...

  4. spring-boot的helloWorld详解

    1.运行环境 开发工具:intellij idea JDK版本:1.8 项目管理工具:Maven 3.2.5 2.Maven Plugin管理 pom.xml配置代码: <project xml ...

  5. JS中数据结构之集合

    集合(set)是一种包含不同元素的数据结构.集合中的元素称为成员.集合的两个最重要特性是:首先,集合中的成员是无序的:其次,集合中不允许相同成员存在.当你想要创建一个数据结构用来保存一些独一无二的元素 ...

  6. php获取linux服务器CPU、内存、硬盘使用率的实现代码

    define("MONITORED_IP", "172.16.0.191"); //被监控的服务器IP地址 也就是本机地址 define("DB_SE ...

  7. BUUCTF | [SUCTF 2019]EasySQL (堆叠注入)

    fuzz ing了一下,发现了一堆过滤: 同时发现了注入点,这个应该是var_dump()函数 ===============第二天的分割线 好吧我放弃了,找了一下wp 正确解:select 1;se ...

  8. 渗透测试工具sqlmap基础教程

    转载请注明出处:http://blog.csdn.net/zgyulongfei/article/details/41017493 作者:羽龍飛 本文仅献给想学习渗透测试的sqlmap小白,大牛请绕过 ...

  9. 控制透明度(兼容IE FF)

    filter:alpha(opacity=70);opacity:0.7;

  10. IDEA 创建 Maven web项目注意事项

    需要设置 Maven ->Runner->VM options -Dmaven.multiModuleProjectDirectory=$M2_HOME 需要添加一个archetypeCa ...