前言

之前开发vue项目,一直是自己搭建脚手架,并没有使用配套的Vue-CLI。一、是3.0之前的CLI无明显优势,配置繁琐;二、是觉得自己从零配置项目可控性更强。
Vue-CLI 3.0 于去年8月份就已发布,却一直没去了解。近日,有新Vue H5项目开发,就想着用Vue CLI3.0脚手架构建项目。并记录一下构建使用过程。

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

CLI3.0 功能优势

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

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

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

image.png

搭建项目

项目git地址:https://github.com/now1then/vue-h5-pro

初始化项目

安装脚手架:

  1. npm install -g @vue/cli
  2. # OR
  3. yarn global add @vue/cli

创建一个项目

  1. vue create my-project
  2. # OR
  3. 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命令以图形化界面创建和管理项目:

  1. 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,则设置:

  1. presets: [
  2. ['@vue/app', {
  3. 'polyfills': [
  4. 'es6.string.includes'
  5. ]
  6. }]
  7. ],

ESLint

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

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

加载mint-ui库

  1. // 安装
  2. npm install mint-ui

按需引入

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

  1. npm install babel-plugin-component -D

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

  1. module.exports = {
  2. presets: [
  3. ['@vue/app', {
  4. 'polyfills': [
  5. 'es6.string.includes'
  6. ]
  7. }]
  8. ],
  9. "plugins": [
  10. ["component", {
  11. "libraryName": "mint-ui",
  12. "style": true
  13. }]
  14. ]
  15. }

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

ajax请求封装

借助Axios库实现http请求

「链接-axios中文文档

安装:
  1. npm install axios;

封装要达成的目标:

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

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

移动端适配

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

  1. 安装postcss-pxtorem插件

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

  1. 根据项目实际情况灵活配置。
  2. 3. 安装`lib-flexible`,并在入口文件main.js中引入

// 安装 lib-flexible yarn add lib-flexible

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

  1. <a name="hOHhd"></a>
  2. ### 引入移动端调试工具
  3. 手机设备上调试H5非常不方便,这时可以引入一个非常好用的调试工具Eruda。
  4. > Eruda 是一个专为手机网页前端设计的调试面板,类似 DevTools 的迷你版,其主要功能包括:捕获 console 日志、检查元素状态、显示性能指标、捕获XHR请求、显示本地存储和 Cookie 信息、浏览器特性检测等等。
  5. > <br />
  6. > 详情请访问-[Github链接](https://github.com/liriliri/eruda)
  7. 可以通过CDN引用,当然也可以下载到项目中直接使用。
  8. 本项目配置中使用CLI环境变量配置,来设置是否加载Eruda。 实际项目使用可灵活配置。<br />`.env.development`文件设置环境变量:

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

  1. index.html文件设置:
  2. ```html
  3. <!--手机调试-->
  4. <% if (VUE_APP_ERUDA === 'true') { %>
  5. <script src="//cdn.bootcss.com/eruda/1.5.2/eruda.min.js"></script>
  6. <script> window.eruda.init(); </script>
  7. <% } %>

效果:
ereda演示.gif

其他CLI配置

postcss.config.js

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

可视化分析工具

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

  1. // vue.config.js
  2. configureWebpack: config => {
  3. // 生产环境打包分析体积
  4. if (process.env.NODE_ENV === 'production' && process.env.npm_config_report) {
  5. return {
  6. plugins: [
  7. new BundleAnalyzerPlugin()
  8. ]
  9. }
  10. }
  11. },

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

统一设置less全局变量

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

安装style-resources-loader
  1. yarn add style-resources-loader -D

vue.config.js 配置
  1. chainWebpack: config => { // CLI内部webpack配置
  2. const types = ['vue-modules', 'vue', 'normal-modules', 'normal']
  3. types.forEach(type => addStyleResource(config.module.rule('less').oneOf(type)))
  4. },
  5. // 全局样式 变量、函数
  6. function addStyleResource (rule) {
  7. rule.use('style-resource')
  8. .loader('style-resources-loader')
  9. .options({
  10. patterns: [
  11. path.resolve(__dirname, 'src/styles/variables.less'),
  12. path.resolve(__dirname, 'src/styles/mixin.less'),
  13. ],
  14. })
  15. }

项目其他改动:

CSS样式初始化

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

引入fastclick

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

  1. // 安装
  2. yarn add fastclick
  3. // main.js中引入使用
  4. import FastClick from 'fastclick';
  5. FastClick.attach(document.body);

项目开发

目录结构

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

image.png
项目开发都在src目录下;

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

Vue模块说明

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

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

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

  1. <script>
  2. import { formatDate } from '@/utils/cloud-utils';
  3. import MainButton from '@/component_basics/MainButton';
  4. import transferDom from '@/directives/transferDom';
  5. export default {
  6. name: 'demoPage',
  7. components: { // 组件
  8. MainButton
  9. },
  10. directives: { // 指令
  11. transferDom
  12. },
  13. filters: { // 过滤器
  14. formatDate
  15. },
  16. data() {
  17. return {
  18. title: '测试'
  19. }
  20. },
  21. methods: {}
  22. }
  23. </script>

Router路由封装

  1. 路由懒加载

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

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

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

  1. // 部分路由信息
  2. {
  3. path: '/demo',
  4. name: 'demo',
  5. component: Demo,
  6. meta: {
  7. title: '演示Demo', // 标题
  8. requireAuth: true, // 登录权限
  9. keepAlive: false,
  10. }
  11. },
  12. //路由拦截
  13. // 路由导航守卫
  14. router.beforeEach((to, from, next) => {
  15. // 登录权限
  16. if (to.meta.requireAuth) { // 判断是否校验登录权限
  17. if (!window.userName) { // 判断是否登录,根据实际业务处理
  18. next({
  19. path: '/login',
  20. query: {
  21. redirect: to.fullPath // 未登录,跳转到登录页,登录后跳转回当前页。
  22. }
  23. })
  24. return;
  25. }
  26. }
  27. // 路由发生变化修改页面title
  28. if (to.meta.title) {
  29. document.title = to.meta.title + ' | vue-h5-pro';
  30. } else {
  31. document.title = 'vue-h5-pro';
  32. }
  33. next()
  34. })

页面平滑切换动画

效果图:
页面切换动画.gif

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

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

  1. // 路由拦截 router.js
  2. router.afterEach((to, from) => {
  3. // console.log(to, from);
  4. if (!(from.path === '/' && from.name === null)) {
  5. setLocalRoute(to, from)
  6. }
  7. });
  8. function setLocalRoute(to, from) {
  9. // 本地已访问页面路由,存5条
  10. const localRoute = window.myVue.localRoute = window.myVue.localRoute || [];
  11. const from_index = localRoute.indexOf(from.path);
  12. const to_index = localRoute.indexOf(to.path);
  13. if (from_index < 0) {
  14. localRoute.unshift(from.path);
  15. to_index >= 0 && localRoute.splice(to_index, 1)
  16. }
  17. if (localRoute.length > 5) {
  18. localRoute.splice(0, 1)
  19. }
  20. }

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

  1. <template>
  2. <div id="app">
  3. <transition :name="direction">
  4. <router-view class="page" />
  5. </transition>
  6. </div>
  7. </template>
  8. <script>
  9. export default {
  10. name: 'App',
  11. data() {
  12. return {};
  13. },
  14. computed: {
  15. // 动态设置过渡样式
  16. direction: function() {
  17. const currentPath = this.$route.path;
  18. const localRoute = (window.myVue && window.myVue.localRoute) || [];
  19. // console.log(localRoute, currentPath);
  20. const index = localRoute.indexOf(currentPath);
  21. let tranName = '';
  22. if (localRoute.length === 0) {
  23. tranName = 'fade'; // 首页,渐显
  24. } else if (index >= 0) {
  25. tranName = 'page-back'; // 回退,右划
  26. } else {
  27. tranName = 'page-go'; // 进入新页面,左划
  28. }
  29. return tranName;
  30. }
  31. },
  32. };
  33. </script>
  34. <style lang="less">
  35. .page {
  36. position: absolute;
  37. width: 100%;
  38. height: 100%;
  39. transition: all 0.8s ease-in-out;
  40. }
  41. .page-go-enter-active {
  42. transform: translate3d(100%, 0, 0);
  43. }
  44. .page-go-enter-to {
  45. transform: translate3d(0, 0, 0);
  46. }
  47. .page-go-leave-active {
  48. transform: translate3d(0, 0, 0);
  49. }
  50. .page-go-leave-to {
  51. transform: translate3d(-100%, 0, 0);
  52. }
  53. .page-back-enter-active {
  54. transform: translate3d(-100%, 0, 0);
  55. }
  56. .page-back-enter-to {
  57. transform: translate3d(0, 0, 0);
  58. }
  59. .page-back-leave-active {
  60. transform: translate3d(0, 0, 0);
  61. }
  62. .page-back-leave-to {
  63. transform: translate3d(100%, 0, 0);
  64. }
  65. }
  66. </style>

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

通用化组件(更新…)

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

Tip提示组件

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

项目链接

Github链接:https://github.com/now1then/vue-h5-pro
Vue-CLI3搭建移动端H5应用-语雀:https://www.yuque.com/nowthen/longroad/vue_cli3
Vue-CLI3搭建移动端H5应用-掘金:https://juejin.im/post/5d674d87e51d4561fa2ec0a6

待完善点:

  • 制定代码及命名规范
  • 项目打包上传配置
  • 完善通用化组件
  • 引入mock平台

  • 国际化语言配置

  • 提取单独的PC端项目配置
  • 考虑支持多页