详解基于vue-cli3.0如何构建功能完善的前端架子


Posted in Javascript onOctober 09, 2018

上一篇文章写了vue和typescript的整合,发现很多小伙伴对vue-cli构建出来的项目很感兴趣,所以今天打算写写怎么在vue-cli3.0的架子上,在进一步完善,整合出具备基础功能的前端架子,主要包括以下几个功能点:

  1. webpack 打包扩展
  2. css:sass支持、normalize.css
  3. rem布局
  4. 路由设计:懒加载、前置检查、合法性校验
  5. api 设计
  6. 请求体设计-防重复提交
  7. vuex状态管理

webpack 打包扩展

vue-cli3 最大的特点就是 零配置 ,脚手架把webpack相关的配置都隐藏在@vue\preload-webpack-plugin中,默认的配置可以满足大部分应用场景,优点是我们可以节省很多折腾配置的时间,webpack对于新手来说,还是有点门槛的,这样一来,新人上手可以更关注于vue的编码上。缺点也很明显,对于想自己进行自定义配置的时候,就会稍微麻烦些。

查看当前webpack的详细配置

使用 vue inspect 可以查看到详细的配置列表

扩展webpack配置

当我们想要修改或者扩展webpack配置项时,可以在根目录下新增 vue.config.js 文件,列举个我自己写的简单小栗子

// webpack 扩展
module.exports = {
 baseUrl: 'production' === process.env.NODE_ENV ?
  '/production-sub-path/' :
  '/',
 chainWebpack: config => {
  config.module
   .rule('images')
   .use('url-loader')
   .tap(options => Object.assign(options, { limit: 500 }));
 },
 devServer: {
  open: 'darwin' === process.platform,

  // host: '0.0.0.0',
  port: 8088,
  https: false,
  hotOnly: false,

  // proxy: 'https://api.douban.com' // string | Object 
  proxy: 'http://localhost:3000' // string | Object 
 },
 lintOnSave: false
};

官网Vue.js 开发的标准工具 的介绍非常详细,而且还有中文版,非常易懂,

sass支持

<style lang="scss"></style>
<style lang="scss">
@import "./assets/style/app";
</style>

在组件中使用自定义的 functions 和 mixin,我暂时没找到全局引用的办法,只能在需要使用的组件文件中手动引用,如下

<style lang="scss">
@import "../assets/style/functions";
@import "../assets/style/mixin";
.rem {
 height: px2rem(187.5px); //自定义的函数
}
.mimi {
 @include clearfix(); //自定义的mixin
}
</style>

为了抹平各个浏览器间的差异,我们需要引入 normalize.css

// app.scss
@import "./node_modules/normalize.css/normalize"; //引用第三方normalize
@import "custom_normalize"; // 自定义的normalize

rem布局

在移动端下使用rem布局是个不错的选择,既然我们使用里的scss,那么可以使用函数来简化我们的重复计算的工作。设计给到的通常是2倍图,宽为750px,那么我们可以将基准设为 document.getElementsByTagName('html')[0].style.fontSize = window.innerWidth / 10 + 'px'; 然后写个转换函数,如下:

// _functions.scss
@function px2rem($px) {
 $rem: 75px;
 @return ($px/$rem) + rem;
}

我们在使用的时候,就可以这么写

.rem {
 height: px2rem(300px); // 2倍图下的宽是300px,
}

转换成css就是

.rem {
 height: 4rem;
}

路由设计

主要包括路由懒加载、路由前置检查、合法性校验逻辑,以下是我写的一个简单路由

import Vue from 'vue';
import Router from 'vue-router';

// 路由懒加载
const getComponent = (name: string) => () => import(`./views/${name}.vue`);

Vue.use(Router);

const router = new Router({
 routes: [
  {
   path: '/',
   name: 'home',
   component: getComponent('home')
  },
  {
   path: '/about',
   name: 'about',
   component: getComponent('about'),
   meta: {
    auth: true
   }
  },
  {
   path: '*',
   name: 'not_fount',
   component: getComponent('notFount')
  }
 ]
});

/**
 * 路由前置检查
 */
router.beforeEach((to, from, next) => {
 // 合法性校验
 if (to.meta.auth) {
  console.log('into auth');
  next();
 }
 next();
});
export default router;

api 设计

新建 service 文件夹用于存放api脚本,根据业务模块来划分文件,如用户相关的api一个文件、购买相关的一个文件, api.ts 是各模块api的集合,如下

// service/api.ts
export { userApi } from './user';
export { buyApi } from './buy';

// service/user.ts
export const userApi = {
 /**
  * 获取用户数据
  */
 userInfo: '/node_api/read/userInfo'
};
// service/buy.ts
export const buyApi = {
 /**
  * 购买
  */
 shoping: '/node_api/shop/buy'
};

这么划分,是为了项目结构和业务结构都足够清晰,同时可以避免单文件过长的问题。

HTTP请求二次封装

发送http我使用的是非常流行的 axios ,我在其基础上,稍微进行简单的封装,然后暴露 request 对象供调用。二次封装主要是为了解决以下几个问题

  1. 简化参数,把一些常用参数都赋默认值,简化外部的使用,使得更加通用和利于排查问题。
  2. 返回报文统一处理,我们通常需要对些高频的场景做相同的处理,如错误码、未登录等场景,可以在它提供的返回响应拦截器中,统一处理。
  3. 防止重复提交,因为网络、后端处理的因素,有时接口响应会较慢,那么用户可能会在非常短的时间内,反复点击按钮,在第一次请求未返回的情况下,会再次发起新的请求,那么我们可以在axios提供的前置拦截器中搞点事情。关于防止重复请求这东东,我在以前的一篇文章有写过, 前端防止用户重复提交-js 感兴趣的小伙伴可以看看。

根据以上几点,下面是我封装的request文件,思路都比较简单,就不多说啦

import axios from 'axios';
import qs from 'qs';

const Axios = axios.create({
 baseURL: '/',
 timeout: 10000,
 responseType: 'json',
 withCredentials: true,
 headers: {
  'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8'
 }
});
const CancelToken = axios.CancelToken;
const requestMap = new Map();

// 请求前置拦截器
Axios.interceptors.request.use(
 config => {

  // 防重复提交
  const keyString = qs.stringify(Object.assign({}, { url: config.url, method: config.method }, config.data));
  if (requestMap.get(keyString)) {
   // 取消当前请求
   config.cancelToken = new CancelToken((cancel) => {
    cancel('Please slow down a little');
   });
  }
  requestMap.set(keyString, true);
  Object.assign(config, { _keyString: keyString });

  if (config.method === 'post' || config.method === 'put' || config.method === 'delete') {
   // 序列化
   config.data = qs.stringify(config.data);
  }

  return config;
 },
 error => {
  return Promise.reject(error);
 }
);

// 返回响应拦截器
Axios.interceptors.response.use(
 res => {
  // 重置requestMap
  const config: any = res.config;
  requestMap.set(config._keyString, false);

  if (res.status === 200) {
   return res.data;
  }
  // todo 弹窗提示等
  console.log(`request error:${res}`);
 },
 error => {
  return {
   code: -1
  };
 }
);

/**
 * @description
 * 请求
 * @param url
 * @param data
 * @param method
 */
const request = (url: string, data = {}, method = 'post') => {
 return Axios({
  method,
  url,
  data,
  params: method.toUpperCase() === 'GET' && data
 });

};

export { request };

vuex状态管理

这里我根据业务模块来划分文件结构,如下图

详解基于vue-cli3.0如何构建功能完善的前端架子

分为首页模块和用户模块,每个模块都有自己独立的 state mutations 等,在 store.ts 中,引入各模块的文件,如下

import Vue from 'vue';
import Vuex from 'vuex';
import index from './indexModule/index';
import user from './userModule/user';

Vue.use(Vuex);

export default new Vuex.Store({
 modules: {
  user,
  index
 }
});

大家注意到这里有个 store_types.ts 文件,这个文件主要是为了搭配ts使用的,文件内容如下

export enum UserType {
 /**
  * 模块名称
  */
 'MODULE_NAME' = 'user',
 /**
  * 增加次数
  */
 'ADD_COUNT' = 'addCount',
 /**
  * 计算属性-获取十倍的值
  */
 'GET_TEM_COUNT' = 'getTenCount'
}

在看下组件中的使用方式:

<script lang="ts">
import { UserType } from '@/store/store_types';
import { Component, Prop, Vue, Watch,Emit } from 'vue-property-decorator';
import {
 Action,
 Getter,
 Mutation,
 namespace,
 State
} from 'vuex-class';

@Component
export default class Test extends Vue {

 @State(state => state[UserType.MODULE_NAME].count) public fff!: number;

 @Getter(`${UserType.MODULE_NAME}/${UserType.GET_TEM_COUNT}`) public tenCount!: number;

 @Mutation(`${UserType.MODULE_NAME}/${UserType.ADD_COUNT}`) public addCount!: any;

}
</script>

虽然这么写的确有点绕,但有个好处,我们可以通过注释清晰知道方法和属性的说明

小结

以上是我根据自己工作中常见的场景来设计的,希望能对小伙伴能有帮助,其中设计不当的地方,欢迎小伙伴们在留言区一起探讨哈~也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
js no-repeat写法 背景不重复
Mar 18 Javascript
Javascript获取窗口(容器)的大小及位置参数列举及简要说明
Dec 09 Javascript
轻量级网页遮罩层jQuery插件用法实例
Jul 31 Javascript
解决jquery中动态新增的元素节点无法触发事件问题的两种方法
Oct 30 Javascript
Js类的静态方法与实例方法区分及jQuery拓展的两种方法
Jun 03 Javascript
高效的jQuery代码编写技巧总结
Feb 22 Javascript
Node接收电子邮件的实例代码
Jul 21 Javascript
浅谈vue的几种绑定变量的值 防止其改变的方法
Mar 01 Javascript
解决vue打包项目后刷新404的问题
Mar 06 Javascript
如何理解Vue的v-model指令的使用方法
Jul 19 Javascript
jQuery实现的简单拖拽功能示例【测试可用】
Aug 14 jQuery
详解vue使用$http服务端收不到参数
Apr 19 Javascript
angularJS自定义directive之带参方法传递详解
Oct 09 #Javascript
Vue中的作用域CSS和CSS模块的区别
Oct 09 #Javascript
利用JS动态生成隔行换色HTML表格的两种方法
Oct 09 #Javascript
对angularJs中自定义指令replace的属性详解
Oct 09 #Javascript
基于vue2.0的活动倒计时组件countdown(附源码下载)
Oct 09 #Javascript
Vue中的$set的使用实例代码
Oct 08 #Javascript
js根据json数据中的某一个属性来给数据分组的方法
Oct 08 #Javascript
You might like
php 记录进行累加并显示总时长为秒的结果
2011/11/04 PHP
PHP中使用unset销毁变量并内存释放问题
2012/07/05 PHP
自己写的php curl库实现整站克隆功能
2015/02/12 PHP
Laravel框架中Blade模板的用法示例
2017/08/30 PHP
jQuery如何获取同一个类标签的所有值(默认无法获取)
2014/09/25 Javascript
JS实现至少包含字母、大小写数字、字符的密码等级的两种方法
2015/02/03 Javascript
javascript为按钮注册回车事件(设置默认按钮)的方法
2015/05/09 Javascript
JS实现网页上随滚动条滚动的层效果代码
2015/11/04 Javascript
JavaScript学习笔记整理_简单实现枚举类型,扑克牌应用
2016/09/19 Javascript
vue-router:嵌套路由的使用方法
2017/02/21 Javascript
jQuery滑动到底部加载下一页数据的实例代码
2017/05/22 jQuery
浅谈关于.vue文件中style的scoped属性
2017/08/19 Javascript
JavaScript事件处理程序详解
2017/09/19 Javascript
在Vue中使用highCharts绘制3d饼图的方法
2018/02/08 Javascript
JS实现的简单下拉框联动功能示例
2018/05/11 Javascript
Vue scrollBehavior 滚动行为实现后退页面显示在上次浏览的位置
2019/05/27 Javascript
vue-cli3跨域配置的简单方法
2019/09/06 Javascript
[42:27]DOTA2上海特级锦标赛主赛事日 - 3 败者组第三轮#2Fnatic VS OG第三局
2016/03/05 DOTA
python正则表达式match和search用法实例
2015/03/26 Python
python 循环遍历字典元素的简单方法
2016/09/11 Python
使用pyecharts在jupyter notebook上绘图
2020/04/23 Python
python数据结构之链表的实例讲解
2017/07/25 Python
python自带tkinter库实现棋盘覆盖图形界面
2019/07/17 Python
django一对多模型以及如何在前端实现详解
2019/07/24 Python
解决pyCharm中 module 调用失败的问题
2020/02/12 Python
python turtle工具绘制四叶草的实例分享
2020/02/14 Python
Python中pyecharts安装及安装失败的解决方法
2020/02/18 Python
python中K-means算法基础知识点
2021/01/25 Python
英国女性化妆品收纳和家具网站:Beautify
2019/12/07 全球购物
澳大利亚领先的亚麻品牌:Bed Threads
2019/12/16 全球购物
会计学个人自荐信模板
2013/12/13 职场文书
三年级语文教学反思
2014/02/01 职场文书
幼儿园五一活动方案
2014/02/07 职场文书
2016春季运动会前导词
2015/11/25 职场文书
MySQL安装后默认自带数据库的作用详解
2021/04/27 MySQL
Python图片检索之以图搜图
2021/05/31 Python