基于Vue CLI3 搭建五脏俱全的移动端H5应用

15,277 阅读12分钟

前言

之前开发vue项目,一直是自己搭建脚手架,并没有使用配套的Vue-CLI。原因是:

一、是3.0之前的CLI无明显优势,配置繁琐;

二、是觉得自己从零配置项目可控性更强。

Vue-CLI 3.0 于去年8月份就已发布,却一直没去了解。近日,有新Vue H5项目开发,就想着用Vue CLI3.0脚手架构建项目。并记录一下构建使用过程。

项目特性

  • 基于@vue/cli3
  • CSS 预编译语言:less,全局样式初始化
  • ajax封装,借助Axios库实现http请求
  • vue-router封装;懒加载、拦截
  • 页面平滑切换
  • 通用化组件
  • 移动端适配
  • 可视化分析工具
  • 项目结构拆分 等...

使用脚手架最好的参考就是官方文档,官方文档整体还是比较清晰明了的,更新也很及时。
链接-官方文档

CLI3.0 功能优势

Vue CLI 致力于将 Vue 生态中的工具基础标准化。它确保了各种构建工具能够基于智能的默认配置即可平稳衔接,这样你可以专注在撰写应用上,而不必花好几天去纠结配置的问题。与此同时,它也为每个工具提供了调整配置的灵活性,无需 eject。

Vue-CLI3.0旨在近一步简化Vue项目配置,“傻瓜式”配置,促进团队开发的统一和规范化。同时也完全可以是可配置的,保留足够的扩展性。

  • 功能丰富,实现开箱即用,不用再考虑繁琐的配置。
  • 灵活的插件化机制,增强扩展性。
  • 可以通过配套的图形化界面创建、管理项目的配置。有时看着还是很直观舒服的。
  • 支持直接将一个vue文件跑起来,进行快速原型开发。
  • 不用过多担心配置的更新迭代造成的连带影响。
  • vue团队出品,很好地维护并跟进官方的最佳实践和前沿技术。

image.png

搭建项目

项目git地址:github.com/now1then/vu…

初始化项目

克隆项目

git clone git@github.com:now1then/vue-h5-pro.git

安装脚手架:

npm install -g @vue/cli
# OR
yarn global add @vue/cli

创建一个项目

vue create my-project
# OR
vue ui  // 通过图形化界面创建项目
  • 选择preset特性(这里选择更多功能):

image.png

  • 选择需要安装的(Babel、Router、Vuex、Pre-processors、Linter / Formatter):

这里先不引入TypeScript和单元测试。
    

image.png

  • 路由是否使用history模式(Yes)

image.png

  • 选择 CSS 预处理器(Less):

image.png

  • 选择ESLint 配置(ESLint + Standard config)标准配置:

image.png

  • 选择什么时候执行ESLint校验(Lint on save):

image.png

  • 选择以什么样的形式配置以上所选的功能,单独生成配置文件 or 附加到package.json中(In dedicated config files):

image.png

  • 是否将之前的设置保存为一个预设模板(y):

image.png

如果选择 y 后会让输入名称,在下次初始化项目可以使用该预设模板快速构建项目。

运行项目

了解CLI插件命令

启动开发服务器运行项目:**npm run serve**;
命令会启动一个开发服务器 (基于 webpack-dev-server) 并附带开箱即用的模块热重载 (Hot-Module-Replacement)。

image.png

生产环境打包:npm run build;

图形化界面

通过 vue ui命令以图形化界面创建和管理项目:

vue ui

运行成功后,在打开的页面中可以新建项目,也可以导入已有项目。

管理界面:

image.png

目录介绍:
**项目仪表盘:**自定义展现一些的功能小部件;
**插件:**可以查看已安装的CLI插件,还可以搜索安装插件;
**依赖:**可以查看和管理项目的运行依赖和开发依赖;
**配置:**项目的配置项配置管理,包括Vue-CLI配置和ESLint配置等。
**任务:**可以执行对应任务(对应于package.json中脚本命令),方便查看运行结果和分析检查。

图形化界面虽然对于实际开发意义不大,但简洁直观,实际体验还是不错的。具体功能最好运行亲自体验。

主流配置插件

Babel

CLI中已预设@vue/cli-plugin-babel,它默认使用 Babel 7 + babel-loader + @vue/babel-preset-app,但是可以通过 babel.config.js 配置使用任何其它 Babel 预设选项或插件。
『当前仅默认配置就够用,后续实际使用时逐步增加』

默认会转换_es6.promise、_``_es6.symbol_等常见ES6语法,对于未引入的语法,采用"显式地列出了需要的 polyfill的方式"。比如项目中使用es6.string.includes,则设置:

  presets: [
    ['@vue/app', {
      'polyfills': [
        'es6.string.includes'
      ]
    }]
  ],

ESLint

@vue/cli-plugin-eslint
『当前仅适用默认配置就够用。』,具体根据个人习惯和项目规范调整即可。
比如,本人一般关闭以下设置:

  rules: {
    'no-console': process.env.NODE_ENV === 'production' ? 'warning' : 'off',
    'no-debugger': process.env.NODE_ENV === 'production' ? 'warning' : 'off',
    'semi': 0, // 语句结尾分号
    'camelcase': 0, //驼峰命名
    'comma-dangle': 0, // 对象最后逗号
    'space-before-function-paren': 0, // 函数定义前,括号前分割
  },

加载mint-ui库

// 安装
npm install mint-ui
按需引入

借助 babel-plugin-component,我们可以只引入需要的组件,以达到减小项目体积的目的。
首先,安装 babel-plugin-component:

npm install babel-plugin-component -D

然后,将babel.config.js 修改为:

module.exports = {
  presets: [
    ['@vue/app', {
      'polyfills': [
        'es6.string.includes'
      ]
    }]
  ],
  "plugins": [
    ["component", {
      "libraryName": "mint-ui",
      "style": true
    }]
  ]
}

这样使用组件就可以按需引入了。

ajax请求封装

借助Axios库实现http请求

「链接-axios中文文档

安装:
npm install axios;

封装要达成的目标:

  • 统一维护管理接口;
  • 支持接口代理转发;
  • 读取环境配置,区分处理环境。
  • 拦截请求和响应,处理登录超时、404等异常情况;
  • 根据请求的配置匹配接口URL前缀且作支持做特殊处理。

具体详见本人另一篇文章:
漫漫长路-Axios封装

移动端适配

考虑浏览器兼容性和使用习惯,移动端适配还是手淘模式,采用px转remlib-flexible实现。
postcss-pxtorem插件把配置的px转成rem;
lib-flexible库则根据页面尺寸和dpr,自动设置html的字体和meta缩放比例。

  1. 安装postcss-pxtorem插件
yarn add postcss-pxtorem -D
  1. postcss.config.js中增加pxtorem配置
// postcss.config.js
module.exports = {
  plugins: {
    "autoprefixer": {}, 用来自动处理浏览器前缀的一个插件。
    "postcss-
    ": {
      "rootValue": 37.5, // 设计稿宽度的1/10
      "unitPrecision": 5, //小数位
      "minPixelValue": 1, //转换的最小单位
      "selectorBlackList": [], //忽略的样式, 正则
      "propList": ["*"] // 需要做转化处理的属性,如`hight`、`width`、`margin`等,`*`表示全部,正则
    }
  }
}

根据项目实际情况灵活配置。

  1. 安装lib-flexible,并在入口文件main.js中引入
// 安装 lib-flexible
yarn add lib-flexible

// main.js中引入lib-flexible
import 'lib-flexible';

引入移动端调试工具

手机设备上调试H5非常不方便,这时可以引入一个非常好用的调试工具Eruda。

Eruda 是一个专为手机网页前端设计的调试面板,类似 DevTools 的迷你版,其主要功能包括:捕获 console 日志、检查元素状态、显示性能指标、捕获XHR请求、显示本地存储和 Cookie 信息、浏览器特性检测等等。
详情请访问-Github链接

可以通过CDN引用,当然也可以下载到项目中直接使用。

本项目配置中使用CLI环境变量配置,来设置是否加载Eruda。 实际项目使用可灵活配置。
.env.development文件设置环境变量:

VUE_APP_ERUDA=false  # ture表示启用Eruda调试工具

index.html文件设置:

<!--手机调试-->
<% if (VUE_APP_ERUDA === 'true') { %>
  <script src="//cdn.bootcss.com/eruda/1.5.2/eruda.min.js"></script>
  <script> window.eruda.init(); </script>
  <% } %>

效果:

ereda演示.gif

其他CLI配置

postcss.config.js

postcss-import:该插件主要是解决@import引入路径问题。引入本地文件、node_modules等的文件。
postcss-url:该插件主要用来处理文件,比如图片文件、字体文件等引用路径的处理。
autoprefixer:用来自动处理浏览器前缀的一个插件。

可视化分析工具

利用可视化资源分析工具插件webpack-bundle-analyzer 分析生产文件打包大小。

// vue.config.js  
configureWebpack: config => {
    // 生产环境打包分析体积
    if (process.env.NODE_ENV === 'production' && process.env.npm_config_report) {
      return {
        plugins: [
          new BundleAnalyzerPlugin()
        ]
      }
    }
  },

终端命令:yarn build --report
当然图形化界面任务->build->分析 中也可以粗略分析打包大小。

统一设置less全局变量

参考「链接-Vue-CLI3 css自动导入」。
利用style-resources-loader插件,给Vue单文件自动全局导入配置路径中的LESS变量和mixin函数。 这样在使用时不用每个文件都单独引入,就可以直接使用定义的Less变量。

安装style-resources-loader
yarn add style-resources-loader -D
vue.config.js 配置
chainWebpack: config => {   // CLI内部webpack配置
  const types = ['vue-modules', 'vue', 'normal-modules', 'normal']
  types.forEach(type => addStyleResource(config.module.rule('less').oneOf(type)))
},
// 全局样式 变量、函数
function addStyleResource (rule) {
  rule.use('style-resource')
    .loader('style-resources-loader')
    .options({
      patterns: [
        path.resolve(__dirname, 'src/styles/variables.less'),
        path.resolve(__dirname, 'src/styles/mixin.less'),
      ],
    })
}

项目其他改动:

CSS样式初始化

引入_normalize.css_ 、_minireset.css _+ 自定义CSS设置,初始化CSS样式,使HTML元素样式在跨浏览器上表现得的高度一致性。
下载对应文件,直接放到src/assets/styles/路径下。

引入fastclick

移动端点击有300ms延迟,主要是为了解决双击缩放,浏览器等待300ms以判断是否是双击操作。
可以采用引入fastclick.js的方式解决移动端300ms延迟的问题。当然,fastclick.js曾经在老版本手机上解决移动端300ms的问题上做出了很大的贡献。时至今日是否还有必要使用,各方还是各持一词。

// 安装
yarn add fastclick

// main.js中引入使用
import FastClick from 'fastclick';
FastClick.attach(document.body);

项目开发

目录结构

在团队开发时,规范的目录拆分约定,有利于协调开发和项目的长期维护。
根据个人习惯及经验,项目目录构建如下图所示:

image.png

项目开发都在src目录下;

src/assets目录主要存放静态资源文件,比如字体图标、图片、直接引入的或不常变更的第三方库等。

Vue模块说明

全局公共components组件、filter过滤器、directive指令可以直接注入到全局Vue,也可以在使用时页面组件内单独按需注入。

全局组件分为:
components_basics公共基础组件,主要存放封装的与业务无关的基础组件。
components_modules公共业务组件,主要存放提取的可重用业务组件。
对于不重用的业务组件,不用提取到外部,直接存放到具体的页面目录下即可。

页面组件内按需引入模块:

<script>
import { formatDate } from '@/utils/cloud-utils';
import MainButton from '@/component_basics/MainButton';
import transferDom from '@/directives/transferDom';

export default {
  name: 'demoPage',
  components: { // 组件
    MainButton
  },
  directives: { // 指令
    transferDom
  },
  filters: { // 过滤器
    formatDate
  },
  data() {
    return {
      title: '测试'
    }
  },
  methods: {}
}
</script>

Router路由封装

  1. 路由懒加载

链接-Vue Router懒加载
把组件按组分块,结合 Vue 的异步组件和 Webpack 的代码分割功能,实现路由组件的懒加载。

...
const Hello = () => import(/* webpackChunkName: "apply" */ '@/views/hello');
const Demo = () => import(/* webpackChunkName: "demo" */ '@/views/demo');
  1. 路由拦截

通过自定义meta参数,设置路由信息。
利用vue-router导航卫士,路由拦截时,作一些自定义处理。比如登录权限校验、页面标题设置等。

// 部分路由信息
  {
    path: '/demo',
    name: 'demo',
    component: Demo,
    meta: {
      title: '演示Demo', // 标题
      requireAuth: true, // 登录权限
      keepAlive: false,
    }
  },
    
//路由拦截
// 路由导航守卫
router.beforeEach((to, from, next) => {
  // 登录权限
  if (to.meta.requireAuth) { // 判断是否校验登录权限
    if (!window.userName) { // 判断是否登录,根据实际业务处理
      next({
        path: '/login',
        query: {
          redirect: to.fullPath // 未登录,跳转到登录页,登录后跳转回当前页。
        }
      })
      return;
    }
  }
  // 路由发生变化修改页面title
  if (to.meta.title) {
    document.title = to.meta.title + ' | vue-h5-pro';
  } else {
    document.title = 'vue-h5-pro';
  }
  next()
})

页面平滑切换动画

效果图:

页面切换动画.gif

这里实现页面前进/后退时的整屏平滑左划/右划效果。
简单记录5个历史路由,进入新页面有左划切换效果,并记录历史路由;进入历史页面有右划切换效果,并清除历史路由。

router.js文件中拦截路由,记录历史路由信息。

// 路由拦截 router.js
router.afterEach((to, from) => {
  // console.log(to, from);
  if (!(from.path === '/' && from.name === null)) {
    setLocalRoute(to, from)
  }
});

function setLocalRoute(to, from) {
  // 本地已访问页面路由,存5条
  const localRoute = window.myVue.localRoute = window.myVue.localRoute || [];
  const from_index = localRoute.indexOf(from.path);
  const to_index = localRoute.indexOf(to.path);
  if (from_index < 0) {
    localRoute.unshift(from.path);
    to_index >= 0 && localRoute.splice(to_index, 1)
  }
  if (localRoute.length > 5) {
    localRoute.splice(0, 1)
  }
}

main.vue文件中根据路由跳转,动态设置过渡动画样式。

<template>
  <div id="app">
    <transition :name="direction">
      <router-view class="page" />
    </transition>
  </div>
</template>

<script>
export default {
  name: 'App',
  data() {
    return {};
  },
  computed: {
    // 动态设置过渡样式
    direction: function() {
      const currentPath = this.$route.path;
      const localRoute = (window.myVue && window.myVue.localRoute) || [];
      // console.log(localRoute, currentPath);
      const index = localRoute.indexOf(currentPath);
      let tranName = '';

      if (localRoute.length === 0) {
        tranName = 'fade'; // 首页,渐显
      } else if (index >= 0) {
        tranName = 'page-back'; // 回退,右划
      } else {
        tranName = 'page-go'; // 进入新页面,左划
      }
      return tranName;
    }
  },
};
</script>

<style lang="less">
  .page {
    position: absolute;
    width: 100%;
    height: 100%;
    transition: all 0.8s ease-in-out;
  }
  .page-go-enter-active {
    transform: translate3d(100%, 0, 0);
  }
  .page-go-enter-to {
    transform: translate3d(0, 0, 0);
  }
  .page-go-leave-active {
    transform: translate3d(0, 0, 0);
  }
  .page-go-leave-to {
    transform: translate3d(-100%, 0, 0);
  }
  .page-back-enter-active {
    transform: translate3d(-100%, 0, 0);
  }
  .page-back-enter-to {
    transform: translate3d(0, 0, 0);
  }
  .page-back-leave-active {
    transform: translate3d(0, 0, 0);
  }
  .page-back-leave-to {
    transform: translate3d(100%, 0, 0);
  }
}
</style>

项目构建及开发细节后续会持续更新。具体的代码欢迎访问项目查看。

通用化组件(更新...)

此处记录封装的通用化基础组件。
通用化基础组件存放于src/component_basic/目录下。

Tip提示组件

比如:tip提示组件,组件使用及代码详见项目。
效果图:

点击tip.gif

项目链接

Github链接:github.com/now1then/vu…
Vue-CLI3搭建移动端H5应用-语雀:www.yuque.com/nowthen/lon…
Vue-CLI3搭建移动端H5应用-掘金:juejin.cn/post/684490…

待完善点:

  • 制定代码及命名规范

  • 项目打包上传配置

  • 完善通用化组件

  • 引入mock平台

  • 国际化语言配置

  • 提取单独的PC端项目配置

  • 考虑支持多页