MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Vue CLI 最佳实践与代码规范建议

2021-12-302.5k 阅读

一、Vue CLI 基础概述

Vue CLI 是一个基于 Vue.js 进行快速开发的完整系统。它提供了一个交互式的项目脚手架,用于快速搭建新项目,还包含了丰富的插件生态,能方便地进行各种功能扩展,如添加路由、状态管理等。

1.1 安装与初始化项目

要使用 Vue CLI,首先需要安装它。在已经安装了 Node.js 的环境下,通过 npm 或 yarn 进行全局安装:

npm install -g @vue/cli
# 或者使用 yarn
yarn global add @vue/cli

安装完成后,使用 vue create 命令来初始化一个新项目。例如,创建一个名为 my - project 的项目:

vue create my - project

在初始化过程中,Vue CLI 会提供一系列的选项,如选择 Vue 的版本(2.x 或 3.x)、是否安装路由、状态管理等。

1.2 项目结构剖析

一个通过 Vue CLI 创建的典型项目结构如下:

my - project
├── public
│   ├── favicon.ico
│   └── index.html
├── src
│   ├── assets
│   │   └── logo.png
│   ├── components
│   │   └── HelloWorld.vue
│   ├── App.vue
│   ├── main.js
│   ├── router
│   │   └── index.js
│   └── store
│       └── index.js
├── babel.config.js
├── package.json
├── README.md
└── vue.config.js
  • public 目录:包含了在构建过程中不会被处理的静态资源。index.html 是项目的入口 HTML 文件,所有打包后的 JavaScript 和 CSS 文件会被注入到这个 HTML 文件中。
  • src 目录:这是项目的主要源代码目录。assets 用于存放静态资源,如图片、样式文件等;components 存放可复用的 Vue 组件;App.vue 是整个应用的根组件;main.js 是项目的入口 JavaScript 文件,用于创建 Vue 实例并挂载到 DOM 元素上;router 目录管理路由配置;store 目录用于 Vuex 的状态管理配置。
  • babel.config.js:Babel 配置文件,用于将 ES6+ 语法转换为浏览器能识别的语法。
  • package.json:项目的依赖管理文件,记录了项目所依赖的各种包及其版本信息,同时还包含了一些脚本命令,如 npm run serve 用于启动开发服务器,npm run build 用于打包项目。
  • README.md:项目的说明文档,用于向其他开发者介绍项目的功能、使用方法等。
  • vue.config.js:Vue CLI 的配置文件,可以在其中自定义一些构建选项,如修改输出目录、配置代理等。

二、Vue CLI 最佳实践

2.1 构建配置优化

vue.config.js 文件中,可以对项目的构建进行各种优化配置。

2.1.1 生产环境优化

  • 压缩代码:Vue CLI 默认会在生产环境下压缩 JavaScript 和 CSS 代码。但对于一些较大的项目,可以进一步优化压缩配置。例如,使用 terser - webpack - plugin 来优化 JavaScript 压缩,在 vue.config.js 中配置如下:
const TerserPlugin = require('terser - webpack - plugin');

module.exports = {
    chainWebpack: config => {
        config.optimization.minimize(true);
        config.optimization.minimizer('terser').use(TerserPlugin, [{
            parallel: true,
            terserOptions: {
                compress: {
                    drop_console: true // 生产环境移除 console.log
                }
            }
        }]);
    }
};
  • 代码分割:为了避免打包后的文件过大,影响加载性能,可以使用代码分割。Vue CLI 内置了对 splitChunks 的支持。在 vue.config.js 中配置如下:
module.exports = {
    chainWebpack: config => {
        config.optimization.splitChunks({
            chunks: 'all',
            minSize: 30000,
            minChunks: 1,
            automaticNameDelimiter: '~',
            cacheGroups: {
                vendors: {
                    test: /[\\/]node_modules[\\/]/,
                    name: 'chunk - vendors',
                    priority: -10
                },
                common: {
                    name: 'chunk - common',
                    minChunks: 2,
                    priority: -20,
                    reuseExistingChunk: true
                }
            }
        });
    }
};

这样配置后,会将来自 node_modules 的代码和项目中多次引用的公共代码分别打包成单独的文件,在页面加载时可以并行加载这些文件,提高加载速度。

2.1.2 开发环境优化

  • 代理配置:在开发过程中,前端应用通常需要与后端 API 进行交互。由于浏览器的同源策略限制,在开发环境下会遇到跨域问题。可以通过 Vue CLI 的代理功能来解决。在 vue.config.js 中配置如下:
module.exports = {
    devServer: {
        proxy: {
            '/api': {
                target: 'http://localhost:3000', // 后端 API 地址
                changeOrigin: true,
                pathRewrite: {
                    '^/api': ''
                }
            }
        }
    }
};

这样,当前端应用向 /api 开头的地址发送请求时,会被代理到 http://localhost:3000,从而解决跨域问题。

2.2 插件与工具的合理使用

Vue CLI 拥有丰富的插件生态,可以通过 vue add 命令来安装插件。

2.2.1 Vue Router

Vue Router 是 Vue.js 官方的路由管理器。通过 vue add router 命令可以方便地将其添加到项目中。在安装完成后,会在 src/router 目录下生成路由配置文件 index.js。例如,配置一个简单的路由:

import Vue from 'vue';
import Router from 'vue - router';
import Home from '@/views/Home.vue';
import About from '@/views/About.vue';

Vue.use(Router);

export default new Router({
    mode: 'history',
    routes: [
        {
            path: '/',
            name: 'Home',
            component: Home
        },
        {
            path: '/about',
            name: 'About',
            component: About
        }
    ]
});

App.vue 中使用路由:

<template>
    <div id="app">
        <router - link to="/">Home</router - link>
        <router - link to="/about">About</router - link>
        <router - view></router - view>
    </div>
</template>

<script>
export default {
    name: 'App'
};
</script>

<style>
/* 省略样式 */
</style>

2.2.2 Vuex

Vuex 是 Vue.js 应用程序的状态管理模式。通过 vue add vuex 命令安装后,会在 src/store 目录下生成 index.js 文件。例如,创建一个简单的 Vuex 状态管理:

import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

export default new Vuex.Store({
    state: {
        count: 0
    },
    mutations: {
        increment(state) {
            state.count++;
        }
    },
    actions: {
        incrementAsync({ commit }) {
            setTimeout(() => {
                commit('increment');
            }, 1000);
        }
    },
    getters: {
        doubleCount(state) {
            return state.count * 2;
        }
    }
});

在组件中使用 Vuex:

<template>
    <div>
        <p>Count: {{ $store.state.count }}</p>
        <p>Double Count: {{ $store.getters.doubleCount }}</p>
        <button @click="$store.commit('increment')">Increment</button>
        <button @click="$store.dispatch('incrementAsync')">Increment Async</button>
    </div>
</template>

<script>
export default {
    name: 'Counter'
};
</script>

<style>
/* 省略样式 */
</style>

2.2.3 ESLint 与 Prettier

ESLint 是一个用于识别和报告 JavaScript 代码中模式问题的工具,Prettier 是一个代码格式化工具。通过 vue add eslint 命令可以将 ESLint 添加到项目中,并根据需要选择相关的配置选项,如是否使用 Airbnb、Standard 等风格的规则。

为了使 ESLint 和 Prettier 协同工作,需要进行一些额外的配置。在项目根目录下创建 .prettierrc.js 文件,配置 Prettier 的规则:

module.exports = {
    semi: true,
    singleQuote: true,
    trailingComma: 'es5'
};

.eslintrc.js 文件中,添加如下配置,使 ESLint 能够识别 Prettier 的规则:

module.exports = {
    // 省略其他配置
    rules: {
        // 让 ESLint 忽略 Prettier 已经处理的规则
        'prettier/prettier': 'error'
    }
};

这样,在开发过程中,通过 npm run lint 命令可以同时检查代码是否符合 ESLint 和 Prettier 的规则,保持代码风格的一致性。

2.3 多环境配置

在实际开发中,通常需要区分开发环境、测试环境和生产环境,不同环境可能有不同的 API 地址、服务器配置等。Vue CLI 支持通过 .env 文件来进行多环境配置。

2.3.1 环境文件命名规则

  • .env:所有环境都加载的基础配置文件。
  • .env.development:开发环境下加载的配置文件。
  • .env.test:测试环境下加载的配置文件。
  • .env.production:生产环境下加载的配置文件。

2.3.2 配置示例

假设在不同环境下有不同的 API 地址,在 .env.development 文件中配置:

VUE_APP_API_URL=http://localhost:3000/api

.env.production 文件中配置:

VUE_APP_API_URL=https://production - server.com/api

在项目代码中,可以通过 process.env.VUE_APP_API_URL 来获取相应环境的 API 地址。例如,在一个 API 服务的封装文件中:

import axios from 'axios';

const api = axios.create({
    baseURL: process.env.VUE_APP_API_URL
});

export default api;

三、Vue 代码规范建议

3.1 组件命名规范

  • 文件名命名:组件文件名应该采用 PascalCase 命名方式,即首字母大写,每个单词的首字母也大写。例如,MyComponent.vue。这样命名的好处是在文件系统中易于识别,并且与 Vue 组件的注册和使用方式相匹配。
  • 组件注册命名:在 Vue 组件中,无论是全局注册还是局部注册,组件名同样应使用 PascalCase 命名。例如:
// 全局注册
import Vue from 'vue';
import MyComponent from './components/MyComponent.vue';

Vue.component('MyComponent', MyComponent);

// 局部注册
export default {
    components: {
        MyComponent
    }
};
  • 避免使用保留字:组件名应避免使用 JavaScript 的保留字,如 ifforfunction 等,以免在开发过程中出现语法错误或混淆。

3.2 模板语法规范

  • 缩进与换行:在模板中,使用 2 个空格进行缩进,以保持代码的整洁和可读性。每个 HTML 标签尽量单独占一行,特别是对于复杂的组件结构。例如:
<template>
    <div class="container">
        <h1>{{ title }}</h1>
        <ul>
            <li v - for="(item, index) in items" :key="index">{{ item }}</li>
        </ul>
    </div>
</template>
  • 指令使用规范
    • v - bind:当绑定的属性值是一个变量时,可以省略 v - bind 的简写形式 :。例如,:src="imageSrc" 等价于 v - bind:src="imageSrc"。但当绑定的值是一个复杂表达式时,建议使用完整形式,以提高可读性。例如,v - bind:class="{'active': isActive, 'disabled': isDisabled}"
    • v - on:同样,v - on 可以简写为 @。在绑定事件处理函数时,尽量使用方法名而不是内联表达式。例如:
<button @click="handleClick">Click Me</button>
export default {
    methods: {
        handleClick() {
            // 处理逻辑
        }
    }
};
- **v - if 和 v - for**:避免在同一元素上同时使用 `v - if` 和 `v - for`。因为 `v - for` 的优先级高于 `v - if`,会导致不必要的计算。如果确实需要根据条件渲染列表,可以将 `v - if` 放在父元素上,或者使用计算属性过滤列表。例如:
<!-- 不推荐 -->
<li v - for="item in items" v - if="item.isVisible">{{ item.name }}</li>

<!-- 推荐方式一 -->
<ul v - if="filteredItems.length > 0">
    <li v - for="item in filteredItems" :key="item.id">{{ item.name }}</li>
</ul>

<!-- 推荐方式二 -->
<ul>
    <li v - for="item in getVisibleItems" :key="item.id">{{ item.name }}</li>
</ul>
export default {
    data() {
        return {
            items: [
                { id: 1, name: 'Item 1', isVisible: true },
                { id: 2, name: 'Item 2', isVisible: false }
            ]
        };
    },
    computed: {
        filteredItems() {
            return this.items.filter(item => item.isVisible);
        },
        getVisibleItems() {
            return this.items.filter(item => item.isVisible);
        }
    }
};

3.3 样式规范

  • 组件内样式:在 Vue 组件中,推荐使用 <style scoped> 来定义组件内的样式。这样可以确保样式只作用于当前组件,避免样式冲突。例如:
<template>
    <div class="my - component">
        <h1>My Component</h1>
    </div>
</template>

<script>
export default {
    name: 'MyComponent'
};
</script>

<style scoped>
.my - component {
    background - color: lightblue;
}
</style>
  • 全局样式:如果需要定义全局样式,可以在 src/assets 目录下创建一个 styles 文件夹,在其中创建 global.css 文件来存放全局样式。然后在 main.js 中引入:
import Vue from 'vue';
import App from './App.vue';
import './assets/styles/global.css';

Vue.config.productionTip = false;

new Vue({
    render: h => h(App)
}).$mount('#app');
  • CSS 命名规范:采用 BEM(Block - Element - Modifier)命名规范。例如,一个按钮组件的样式命名可以如下:
/* 按钮块 */
.button {
    padding: 10px 20px;
    border: none;
    background - color: blue;
    color: white;
}

/* 按钮上的图标元素 */
.button__icon {
    margin - right: 5px;
}

/* 按钮的禁用状态修饰符 */
.button--disabled {
    background - color: gray;
    cursor: not - allowed;
}

3.4 代码注释规范

  • 组件注释:在每个 Vue 组件的开头,应该添加注释来描述组件的功能、使用方法、props 说明等。例如:
<template>
    <!-- 省略模板内容 -->
</template>

<script>
/**
 * @description 这是一个用户信息展示组件
 * @props {string} name - 用户姓名
 * @props {number} age - 用户年龄
 * @example <UserInfo name="John" age="30" />
 */
export default {
    name: 'UserInfo',
    props: {
        name: {
            type: String,
            required: true
        },
        age: {
            type: Number,
            required: true
        }
    }
};
</script>

<style scoped>
/* 省略样式 */
</style>
  • 方法注释:对于组件中的方法,也应该添加注释说明方法的功能、参数和返回值。例如:
export default {
    methods: {
        /**
         * 计算两个数的和
         * @param {number} num1 - 第一个数
         * @param {number} num2 - 第二个数
         * @returns {number} 两数之和
         */
        addNumbers(num1, num2) {
            return num1 + num2;
        }
    }
};
  • 逻辑注释:在代码逻辑较为复杂的地方,添加注释来解释代码的执行逻辑。例如:
// 过滤出年龄大于 18 岁的用户
const adults = users.filter(user => user.age > 18);

3.5 项目目录结构规范

  • 按功能模块划分:对于较大的项目,建议按功能模块划分目录。例如,一个电商项目可以有如下目录结构:
src
├── components
│   ├── common
│   │   ├── Button.vue
│   │   └── Input.vue
│   └── product
│       ├── ProductList.vue
│       └── ProductDetail.vue
├── views
│   ├── home
│   │   └── Home.vue
│   ├── product
│   │   ├── ProductList.vue
│   │   └── ProductDetail.vue
│   └── cart
│       └── Cart.vue
├── router
│   └── index.js
├── store
│   └── index.js
├── assets
│   ├── images
│   │   └── logo.png
│   └── styles
│       └── global.css
├── main.js

这样的结构使得项目的功能模块清晰,易于维护和扩展。

  • 避免过深的目录嵌套:尽量避免目录嵌套过深,一般不超过 3 层。如果确实需要更细的划分,可以通过命名空间来区分,而不是一味地增加目录层级。例如,在 components 目录下,可以通过前缀来区分不同类型的组件,如 ui - Button.vuefeature - ProductList.vue 等。

3.6 数据管理规范

  • data 函数返回对象:在 Vue 组件中,data 必须是一个函数,并且返回一个对象。这样可以确保每个组件实例都有自己独立的数据副本,避免数据共享导致的问题。例如:
export default {
    data() {
        return {
            count: 0
        };
    }
};
  • 使用 computed 和 watch:合理使用计算属性 computed 和侦听器 watch。计算属性用于依赖响应式数据的复杂计算,并且具有缓存机制,只有依赖的数据发生变化时才会重新计算。例如:
export default {
    data() {
        return {
            firstNumber: 10,
            secondNumber: 20
        };
    },
    computed: {
        sum() {
            return this.firstNumber + this.secondNumber;
        }
    }
};

侦听器用于监听数据的变化,并执行相应的操作。例如,监听一个输入框的值变化,进行实时搜索:

export default {
    data() {
        return {
            searchText: ''
        };
    },
    watch: {
        searchText(newValue) {
            // 根据 newValue 进行搜索操作
        }
    }
};
  • Vuex 状态管理规范:在使用 Vuex 时,要遵循单向数据流原则。状态的修改应该通过 mutations 来进行,而异步操作应该封装在 actions 中。例如:
// store/index.js
import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

export default new Vuex.Store({
    state: {
        user: null
    },
    mutations: {
        setUser(state, user) {
            state.user = user;
        }
    },
    actions: {
        async login({ commit }, credentials) {
            const response = await axios.post('/api/login', credentials);
            commit('setUser', response.data.user);
        }
    }
});

在组件中调用:

<template>
    <div>
        <button @click="loginUser">Login</button>
    </div>
</template>

<script>
export default {
    methods: {
        loginUser() {
            const credentials = { username: 'test', password: 'test' };
            this.$store.dispatch('login', credentials);
        }
    }
};
</script>

<style scoped>
/* 省略样式 */
</style>

通过遵循以上 Vue CLI 最佳实践和代码规范建议,可以提高项目的开发效率、代码质量和可维护性,使 Vue 项目更加健壮和易于扩展。在实际开发中,团队成员应共同遵守这些规范,形成良好的开发习惯。