基于vue-cli3仿制饿了么WebApp项目总结

2018-12-25

使用vue-clie3仿制的饿了么外卖WebApp,这是一个SPA(单页面应用,Single Page Application)。主要技术包括Vue.jsvue-routerAxios,使用better-scroll插件优化页面滚动,css使用stylus编写,采用flex布局、sticky footer等技术。

stiky footer

flex 布局

better-scroll 使用注意事项

  • better-scroll是一个滚动插件,可以查看其中文文档了解更多
  • new新的滚动控制器时,传入的包裹元素,通常情况下内部有唯一一个元素,是实际内容;实际内容溢出包裹元素时就可以滚动;如果内部有多个元素,那么只针对第一个元素。
  • 默认情况下,使用了滚动控制的部分,禁用了点击事件,可以在创建时提供参数项click: true来使用点击;此时点击事件是插件内部触发的,其事件对象有一个_constructed属性;在pc浏览器下,同时原生点击事件也会触发,导致一次点击触发两次事件,为此可以检测该_constructed属性,只响应派发的事件,从而使得移动端和桌面端一致
  • 如果需要监听scroll事件,可以在参数项中控制probeType的值
  • new出来的滚动控制器有refresh()方法,调用时重新计算 better-scroll,当DOM结构发生变化的时候务必要调用确保滚动的效果正常
  • 利用scrollToscrollToElement可以代码触发滚动

vue 中动画注意事项

  • 如果一个元素需要它完成一个动画后,让其执行另一个动画,前一个动画的结束可以利用after-leave钩子事件来监听
  • 利用一个变量来控制动画元素时,如v-show="isShow",如果我们利用代码修改了变量isShow的值,DOM通常不能立即被修改,可以用vm.$nextTick(()=>{...})的方式,使得DOM修改生效后再执行后续任务

配置文件 vue.config.js(vue-cli3.x)

const path = require('path');
const express = require('express');

// mock code
const mockData = require('./mock/data.json');

function resolve(folder) {
  return path.join(__dirname, folder);
}

module.exports = {
  /** 区分打包环境与开发环境
   * process.env.NODE_ENV==='production'  (打包环境)
   * process.env.NODE_ENV==='development' (开发环境)
   * baseUrl: process.env.NODE_ENV==='production'?"https://cdn.didabisai.com/front/":'front/',
   */
  // 基本路径
  baseUrl: '/',
  // 输出文件目录
  outputDir: 'dist',
  // eslint-loader 是否在保存的时候检查
  lintOnSave: true,
  // webpack配置
  // see https://github.com/vuejs/vue-cli/blob/dev/docs/webpack.md
  chainWebpack: () => {},
  configureWebpack: {
    resolve: {
      alias: {
        '@src': resolve('src'),
        '@components': resolve('components')
      }
    }
  },
  //如果想要引入babel-polyfill可以这样写
  // configureWebpack: (config) => {
  //   config.entry = ["babel-polyfill", "./src/main.js"]
  // },

  // 生产环境是否生成 sourceMap 文件
  productionSourceMap: true,
  // css相关配置
  css: {
    // 是否使用css分离插件 ExtractTextPlugin
    extract: true,
    // 开启 CSS source maps?
    sourceMap: false,
    // css预设器配置项
    loaderOptions: {},
    // 启用 CSS modules for all css / pre-processor files.
    modules: false
  },
  // use thread-loader for babel & TS in production build
  // enabled by default if the machine has more than 1 cores
  parallel: require('os').cpus().length > 1,
  // PWA 插件相关配置
  // see https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-pwa
  pwa: {},
  // webpack-dev-server 相关配置
  devServer: {
    open: process.platform === 'darwin',
    host: '127.0.0.1',
    port: 8080,
    https: false,
    hotOnly: false,
    proxy: {
      '/outer': {
        target: 'http://localhost:8080/api/seller',
        onProxyReq(proxyReq, req, res) {
          console.log(proxyReq, req, res);
        },
        onProxyRes(proxyRes, req, res) {
          console.log(proxyRes, req, res);
        },
        pathRewrite: {
          '^/outer': '' // 这里一定要删掉,不然会导致死递归
        }
      }
    },
    before: (app) => {
      const apiRoutes = express.Router();
      const pathArr = ['/seller', '/goods', '/ratings'];
      pathArr.forEach((urlPath) => {
        apiRoutes.get(urlPath, (req, res) => {
          console.log(urlPath);
          res.json({
            errno: 0,
            data: mockData[urlPath.slice(1)]
          });
        });
      });

      app.use('/api', apiRoutes);
    }
  },
  // 第三方插件配置
  pluginOptions: {
    // ...
  }
};

图标代码生成工具

以上两个工具的介绍文章:前端字体图标的使用(阿里、icomoon)

在 vscode 中 stylus 自动格式化插件

安装插件stylus supremacyvscode中使用快捷键ctrl ,打开配置页,进行相关配置(搜索supremacy可以看到 28 项配置),控制自动格式化时是否使用大括号、分号等,vscode中自动格式化快捷键:alt+shift+F

安装 JSONView

这是一个浏览器插件,可以在浏览器中格式化地查看json数据,可参考文章:谷歌浏览器中安装 JsonView 扩展程序。另外实际上在chrome中打开开发这工具,在network中查看相应的请求的response,点击左下角的{}符号,也可以查看格式化好的json数据。

vscode 为.vue 单文件设置模板

File > Preferences > User Snippets,输入vue.json,即可配置,如下:

{
  "Vue Single File": {
    "prefix": "vue",
    "body": [
      "<template>\n",
      "</template>\n",
      "<script>",
      "export default {",
      "  data() {",
      "    return {\n",
      "    }",
      "  },",
      "  components: {\n",
      "  }",
      "}",
      "</script>\n",
      "<style scoped lang=\"stylus\">\n",
      "</style>",
      "$2"
    ],
    "description": "generate snippets for .vue single file"
  }
}

配置好以后,我们新建一个.vue文件,在其中输入vue,然后按Tab键(或者回车键),就会自动填充设置的模板代码。

参考:在 vscode 里使用.vue 代码模板的方法

手机无法通过该 ip 地址访问项目

项目部署 baseUrl

整个项目完成以后,我希望将项目放到github上,这时候配置文件需要有些改动。

vue.config.js中,baseUrl修改如下:

{
  baseUrl: process.env.NODE_ENV === 'production' ? "/order-app/" : '/',
}

这样一来,生产环境下,请求的jscss文件地址就会自动加上order-app,这个目录是项目部署成github项目主页后的根目录,即https://xxxx.github.io/order-app

如果希望在本地运行打包后的项目,需要将dist文件夹放到一个本地服务器中,如apache服务器,也可以自己用node搭建个简单的服务器,那么上面的生产环境下的baseUrl需要修改为项目打包后的文件夹相对服务器根路径的地址,比如服务器根路径为some/path/htdocs,项目打包好后在其下的projects/order-app/dist目录中,那么就将后面这个目录设为baseUrl就可以了。

项目部署 mock 数据处理

在开发环境下,使用了vue-resource进行数据请求,借助了express读取本地根目录下的mock文件夹中的数据文件(详见vue.config.js)。但是这样一来,生产环境下有两个问题:

  • mock文件夹不会被打包到dist
  • express服务器只是用于开发环境的,生产环境不能起作用

为了解决上面的问题,可以将数据文件作为静态资源,直接放在public目录下,这样就会被打包到dist目录中,由于修改了位置,vue.config.js中需要将require('./mock/data.json')修改为require('./public/mock/json');另外需要为生产环境的ajax请求做些封装。在不修改之前的源码的情况下,使用了axios替代vue-resource,只需要在入口文件中设置Vue.prototype.$http = axios;,就可以仍旧使用原来的vm.$http.get(...).then(...)的接口,不过获取的响应结果的结构可能略有不同,需要稍微修改下处理代码(此处不详述)。

另一个问题,现在还没解决,开发环境使用express处理/api/xxx这样数据请求,但是打包后的文件夹可没有对应的/api/xxx(要注意:github项目主页是静态网站),为此我们需要在生产环境中将这个请求拦截,修改请求地址,并对请求结果做预处理,这就需要使用axiosinterceptors接口。以以上分析为基础,我们在src下新建api文件夹,新建config.js,内容如下:

import axios from 'axios';

// 设置分别设置开发环境和生产环境下axios的全局baseURL默认参数
if (process.env.NODE_ENV === 'development') {
  // 开发环境
  axios.defaults.baseURL = '/';
} else {
  // 把项目放到github上发布后的根地址
  axios.defaults.baseURL = '/order-app/';

  // 这是项目本地build后的地址(前面的projects是由于我把整个项目放在了本地的apache服务器htdocs根目录下的projects子目录中
  // 额外说明:开发环境下用的是vue-cli提供的本地服务器,端口是设定的8080,apache服务不起作用;本地build后,仍然需要一个本地服务器来访问,这时apache服务器就起作用了,端口是默认的80)
  // axios.defaults.baseURL = '/projects/order-app/dist/';
}

// 生成环境下的axios的拦截器(实际上开发环境中也有代理的,在vue.config.js的devServer中配置的)
if (process.env.NODE_ENV === 'production') {
  axios.interceptors.request.use(function(config) {
    // eslint-disable-next-line
    let url = config.url.replace(/api\/([^\/\?]+)/, 'mock/data.json');
    config.url = url;
    config.dataKey = RegExp.$1;
    return config;
  });
  axios.interceptors.response.use(function(res) {
    res.data = {
      errNum: 0,
      data: res.data[res.config.dataKey]
    };
    return res;
  });
}

在生产环境下,项目的根目录是order-app,将其设置为axios请求的默认全局根路径。接着,在生产环境下,对请求和响应都进行拦截,将请求的api/xxx路径修改为对应的静态json文件的路径;并对响应的数据结果,更具请求进行了预处理(和开发环境中express对数据的预处理类似)。

接着,我们只需要在main.jsimport @src/api/config;就可以了。

参考:vue 打包后如何区分开发、测试和生产等不同的开发环境

开发环境下手机端调试

在手机和电脑在同一局域网的情况下,可以通过直接访问电脑的ip地址和端口,访问到页面,就和电脑浏览器中访问一样。但是在项目快要完工的时候,突然出现了电脑端完全正常,手机页面空白的情况。

通过插入js文件的方式调试,比较能够确定页面是加载了的,很可能是内部的出现了错误,为此找到一个浏览器端的控制台插件eruda,之后在控制台中看到了use of const in strict mode.报错,查到了类似下面的issue

其原因应该是高版本的webpack-dev-server中使用的es6语法在一些低版本浏览器中不被支持。这和项目代码中的es6语法不一样,它们默认情况下不会被babel这样的插件转义,因为它们用在开发环境中,并不会对生成环境的代码造成影响,这些issue中给出的解决方案是降低webpack-dev-serve的版本。

由于不太想修改vue-cli的开发环境,笔者没有尝试过这一方案,而是使用高版本一些的手机浏览器,发现问题被解决了。

项目部署 gh-pages 分支

打包后的文件在dist文件夹中,为了在github项目主页中直接使用这个子文件中的内容,我们可以另外创建一个gh-pages分支,每次打包都要推送以更新该分支。然后在github中的Setting > Github Pages中选择该分支作为项目主页。

git subtree push --prefix build/dist origin gh-pages

参考:我可以在存储库的子文件夹中拥有我的 Github Pages index.html 吗?

(本文完)

知识共享许可协议
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。