如何基于vue-cli3.0构建功能完善的移动端架子


Posted in Javascript onApril 24, 2019

基于vue-cli3.0构建功能完善的移动端架子,主要功能包括

  • webpack 打包扩展
  • css:sass支持、normalize.css、_mixin.scss、_variables.scss
  • vw、rem布局
  • 跨域设置
  • eslint设置
  • cdn引入
  • 路由设计、登录拦截
  • axios、api 设计
  • vuex状态管理

项目地址: vue-cli3-H5

demo地址: https://zhouyupeng.github.io/vuecli3H5/#/

webpack 打包扩展

vue-cli3.*后目录结构大改,去除了以往的build,config文件夹,要实现配置的改动在根目录下增加vue.config.js进行配置

css:sass支持、normalize.css、_mixin.scss、_variables.scss

使用的css预处理器是sass,对于css mixin,变量这里做了全局引入,并且引入 normalize.css 使HTML元素样式在跨浏览器上表现得的高度一致性

vue.config.js配置

css: {
    // 是否使用css分离插件 ExtractTextPlugin
    extract: true,
    // 开启 CSS source maps?
    sourceMap: false,
    // css预设器配置项
    // 启用 CSS modules for all css / pre-processor files.
    modules: false,
      sass: {
        data: '@import "style/_mixin.scss";@import "style/_variables.scss";' // 全局引入
      }
    }
  }

vw、rem布局

对于移动端适配方案使用的是 网易新闻 的方法,

使用vw + rem布局

/**
750px设计稿
  取1rem=100px为参照,那么html元素的宽度就可以设置为width: 7.5rem,于是html的font-size=deviceWidth / 7.5
**/
html {
  font-size: 13.33333vw
}

@media screen and (max-width: 320px) {
  html {
    font-size: 42.667PX;
    font-size: 13.33333vw
  }
}

@media screen and (min-width: 321px) and (max-width:360px) {
  html {
    font-size: 48PX;
    font-size: 13.33333vw
  }
}

@media screen and (min-width: 361px) and (max-width:375px) {
  html {
    font-size: 50PX;
    font-size: 13.33333vw
  }
}

@media screen and (min-width: 376px) and (max-width:393px) {
  html {
    font-size: 52.4PX;
    font-size: 13.33333vw
  }
}

@media screen and (min-width: 394px) and (max-width:412px) {
  html {
    font-size: 54.93PX;
    font-size: 13.33333vw
  }
}

@media screen and (min-width: 413px) and (max-width:414px) {
  html {
    font-size: 55.2PX;
    font-size: 13.33333vw
  }
}

@media screen and (min-width: 415px) and (max-width:480px) {
  html {
    font-size: 64PX;
    font-size: 13.33333vw
  }
}

@media screen and (min-width: 481px) and (max-width:540px) {
  html {
    font-size: 72PX;
    font-size: 13.33333vw
  }
}

@media screen and (min-width: 541px) and (max-width:640px) {
  html {
    font-size: 85.33PX;
    font-size: 13.33333vw
  }
}

@media screen and (min-width: 641px) and (max-width:720px) {
  html {
    font-size: 96PX;
    font-size: 13.33333vw
  }
}

@media screen and (min-width: 721px) and (max-width:768px) {
  html {
    font-size: 102.4PX;
    font-size: 13.33333vw
  }
}

@media screen and (min-width: 769px) {
  html {
    font-size: 102.4PX;
    font-size: 13.33333vw
  }
}

@media screen and (min-width: 769px) {
  html {
    font-size: 102.4PX;

    #app {
      margin: 0 auto
    }
  }

vue.config.js配置

loaderOptions: {
  postcss: {
    // 这是rem适配的配置
    plugins: [
      require('postcss-px2rem')({
        remUnit: 100
      })
    ]
  }
}

开发时跨域设置

devServer: {
    open: true, // 启动服务后是否打开浏览器
    host: '127.0.0.1',
    port: 8088, // 服务端口
    https: false,
    hotOnly: false,
    proxy: 'https://easy-mock.com/' // 设置代理
  }

配置完后,本地开发环境的axios的baseUrl要写为 '' ,即空字符串。

发布到线上时如果前端代码不是和后台api放在 同源 下的,后台还需做跨域处理,

eslint standard设置

使用的是 JavaScript standard 代码规范,一个好的编码风格它可以帮助减少团队之间的摩擦,代码阅读起来也更加清爽,更加可读性,不要觉得烦,用了都说好。

这是 JavaScript standard 代码规范的全文

自定义配置,在.eslintrc.js里修改,这里是我给出的配置,4个空格缩进,不检查结尾分号,关闭单var 声明,可自行配置

rules: {
  'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
  'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
  indent: [
    'error',
    4,
    {
      SwitchCase: 1
    }
  ],
  semi: 0, // 不检查结尾分号,
  // 强制使用单引号
  quotes: ['error', 'single'],
  // 关闭函数名与后面括号间必须空格规则
  'space-before-function-paren': 0,
  // 关闭var 声明,每个声明占一行规则。
  'one-var': 0
  }

cdn引入

对于 vue、vue-router、vuex、axios等等这些不经常改动的库、我们让webpack不对他们进行打包,通过cdn引入,可以减少代码的大小、也可以减少服务器的带宽

这里使用的是360的cdn,附上一份公共cdn评测文章 点我

vue.config.js配置

const externals = {
  vue: 'Vue',
  'vue-router': 'VueRouter',
  vuex: 'Vuex',
  'mint-ui': 'MINT',
  axios: 'axios'

}

const cdn = {
  // 开发环境
  dev: {
    css: [
      'https://lib.baomitu.com/mint-ui/2.2.13/style.min.css'
    ],
    js: []
  },
  // 生产环境
  build: {
    css: [
      'https://lib.baomitu.com/mint-ui/2.2.13/style.min.css'
    ],
    js: [
      'https://lib.baomitu.com/vue/2.6.6/vue.min.js',
      'https://lib.baomitu.com/vue-router/3.0.1/vue-router.min.js',
      'https://lib.baomitu.com/vuex/3.0.1/vuex.min.js',
      'https://lib.baomitu.com/axios/0.18.0/axios.min.js',
      'https://lib.baomitu.com/mint-ui/2.2.13/index.js'
    ]
  }
}

configureWebpack: config => {
    if (isProduction) {
      // externals里的模块不打包
      Object.assign(config, {
        externals: externals
      })
    
    } else {
      // 为开发环境修改配置...
    }
  },
chainWebpack: config => {
  // 对vue-cli内部的 webpack 配置进行更细粒度的修改。
  // 添加CDN参数到htmlWebpackPlugin配置中, 详见public/index.html 修改
  config.plugin('html').tap(args => {
    if (process.env.NODE_ENV === 'production') {
      args[0].cdn = cdn.build
    }
    if (process.env.NODE_ENV === 'development') {
      args[0].cdn = cdn.dev
    }
    return args
  })
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <!-- DNS预解析 -->
  <link rel="dns-prefetch" href="//lib.baomitu.com" />
  <meta name="viewport"
    content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=0,minimal-ui,viewport-fit=cover" />
  <link rel="icon" href="<%= BASE_URL %>favicon.ico" />
  <!-- 使用CDN加速的CSS文件,配置在vue.config.js下 -->
  <% for (var i in
  htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.css) { %>
  <link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="preload" as="style" />
  <link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="stylesheet" />
  <% } %>

  <title>vuedemo</title>
</head>

<body>
  <noscript>
    <strong>We're sorry but vuedemo doesn't work properly without JavaScript
      enabled. Please enable it to continue.</strong>
  </noscript>
  <div id="app"></div>
  <!-- 使用CDN加速的JS文件,配置在vue.config.js下 -->
  <% for (var i in
  htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.js) { %>
  <script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
  <% } %>

  <!-- built files will be auto injected -->
</body>

</html>

路由设计、登录拦截

const router = new Router({
  routes: [
    {
      path: '/',
      name: 'home',
      component: Home,
      meta: {
        auth: false, // 是否需要登录
        keepAlive: true // 是否缓存组件
      }
    },
    {
      path: '/about',
      name: 'about',
      component: () =>
        import(/* webpackChunkName: "about" */ './views/About.vue'),
      meta: {
        auth: true,
        keepAlive: true
      }
    },
    {
      path: '/login',
      name: 'login',
      component: () =>
        import(/* webpackChunkName: "login" */ './views/login.vue'),
      meta: {
        auth: false,
        keepAlive: true
      }
    },
    {
      path: '*', // 未匹配到路由时重定向
      redirect: '/',
      meta: {
        // auth: true,
        // keepAlive: true
      }
    }
  ]
})

// 全局路由钩子函数 对全局有效
router.beforeEach((to, from, next) => {
  let auth = to.meta.auth
  let token = store.getters['login/token'];

  if (auth) { // 需要登录
    if (token) {
      next()
    } else {
      next({
        name: 'login',
        query: {
          redirect: to.path
        }
      })
    }
  } else {
    next()
  }
})

在meta中设置是否需要登录以及是否缓存当前组件,

在router.beforeEac路由钩子函数中对登录权限判断,没有登录的跳到登录页面,并且把当前页面传过去,登录后跳回这个页面。

对于页面缓存的在app.vue里进行处理

<keep-alive>
  <router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive"></router-view>

axios、api 设计

对于axios的设计主要是请求拦截器, respone拦截器,以及get,post的二次封装

axios.defaults.timeout = 12000 // 请求超时时间
axios.defaults.baseURL = process.env.VUE_APP_BASE_API

axios.defaults.headers.post['Content-Type'] =
  'application/x-www-form-urlencoded;charset=UTF-8' // post请求头的设置
// axios 请求拦截器
axios.interceptors.request.use(
  config => {
    // 可在此设置要发送的token
    let token = store.getters['login/token'];
    token && (config.headers.token = token)
    Indicator.open('数据加载中')
    return config
  },
  error => {
    return Promise.error(error)
  }
)
// axios respone拦截器
axios.interceptors.response.use(
  response => {
    // 如果返回的状态码为200,说明接口请求成功,可以正常拿到数据
    // 否则的话抛出错误 结合自身业务和后台返回的接口状态约定写respone拦截器
    Indicator.close()
    console.log('response', response);
    if (response.status === 200 && response.data.code === 0) {
      return Promise.resolve(response)
    } else {
      Toast({
        message: response.data.msg,
        position: 'middle',
        duration: 2000
      });
      return Promise.reject(response)
    }
  },
  error => {
    Indicator.close()
    const responseCode = error.response.status
    switch (responseCode) {
      // 401:未登录
      case 401:
        break
      // 404请求不存在
      case 404:
        Toast({
          message: '网络请求不存在',
          position: 'middle',
          duration: 2000
        });
        break
      default:
        Toast({
          message: error.response.data.message,
          position: 'middle',
          duration: 2000
        });
    }
    return Promise.reject(error)
  }
)
/**
 * 封装get方法,对应get请求
 * @param {String} url [请求的url地址]
 * @param {Object} params [请求时携带的参数]
 */
function get (url, params = {}) {
  return new Promise((resolve, reject) => {
    axios
      .get(url, {
        params: params
      })
      .then(res => {
        resolve(res.data)
      })
      .catch(err => {
        reject(err.data)
      })
  })
}
/**
 * post方法,对应post请求
 * @param {String} url [请求的url地址]
 * @param {Object} params [请求时携带的参数]
 */
function post (url, params) {
  return new Promise((resolve, reject) => {
    axios
      .post(url, qs.stringify(params))
      .then(res => {
        resolve(res.data)
      })
      .catch(err => {
        reject(err.data)
      })
  })
}

为了方便管理api路径,这里把所以请求都放在了api文件夹下,如

import { get, post } from '@/axios/http.js'
function getIndex (params) {
  return get('/mock/5cb48c7ed491cd741c54456f/base/index', params)
}
function login(params) {
  return post('/mock/5cb48c7ed491cd741c54456f/base/login', params)
}
export {
  getIndex,
  login
}

其他

去除console.log

装uglifyjs-webpack-plugin插件

// 上线压缩去除console等信息
config.plugins.push(
  new UglifyJsPlugin({
    uglifyOptions: {
      compress: {
        warnings: false,
        drop_console: true,
        drop_debugger: false,
        pure_funcs: ['console.log'] // 移除console
      }
    },
    sourceMap: false,
    parallel: true
  })
)

设置alias目录别名

在项目中经常会引用各个地方的文件,配置后可以更加方便的引入了

config.resolve.alias
      .set('assets', '@/assets')
      .set('components', '@/components')
      .set('view', '@/view')
      .set('style', '@/style')
      .set('api', '@/api')
      .set('store', '@/store')

环境变量和模式

在一个产品的前端开发过程中,一般来说会经历本地开发、测试脚本、开发自测、测试环境、预上线环境,然后才能正式的发布。对应每一个环境可能都会有所差异,比如说服务器地址、接口地址、websorket地址…… 等等。在各个环境切换的时候,就需要不同的配置参数,所以就可以用环境变量和模式,来方便我们管理。

.env        # 在所有的环境中被载入
.env.local     # 在所有的环境中被载入,但会被 git 忽略
.env.[mode]     # 只在指定的模式中被载入
.env.[mode].local  # 只在指定的模式中被载入,但会被 git 忽略

自定义的变量VUE_APP_开头,两个特殊的变量:

  • NODE_ENV - 会是 "development"、"production" 或 "test" 中的一个。具体的值取决于应用运行的模式。
  • BASE_URL - 会和 vue.config.js 中的 baseUrl 选项相符,即你的应用会部署到的基础路径。

如我们定义的.env

NODE_ENV = 'development'
BASE_URL = '/'
VUE_APP_BASE_API = ''

.env.production

NODE_ENV = 'production'
BASE_URL = './'
VUE_APP_BASE_API = 'https://easy-mock.com/'

在项目中可以用process.env.VUE_APP_*,如process.env.VUE_APP_BASE_API获取到定义的值

全局引入filter

把多个地方用到的过滤器写在一个js里面,复用代码。

// 过滤日期格式,传入时间戳,根据参数返回不同格式
const formatTimer = function(val, hours) {
  if (val) {
    var dateTimer = new Date(val * 1000)
    var y = dateTimer.getFullYear()
    var M = dateTimer.getMonth() + 1
    var d = dateTimer.getDate()
    var h = dateTimer.getHours()
    var m = dateTimer.getMinutes()
    M = M >= 10 ? M : '0' + M
    d = d >= 10 ? d : '0' + d
    h = h >= 10 ? h : '0' + h
    m = m >= 10 ? m : '0' + m
    if (hours) {
      return y + '-' + M + '-' + d + ' ' + h + ':' + m
    } else {
      return y + '-' + M + '-' + d
    }
  }
}
export default {
  formatTimer
}

main.js引入

import filters from './filters/index'
// 注入全局过滤器
Object.keys(filters).forEach(item => {
  Vue.filter(item, filters[item])
})

使用

{{1555851774 | formatTimer()}}

vue中使用mock.js

查看我以前写的文章点击我

wepback的可视化资源分析工具插件---webpack-bundle-analyzer

用来分析哪些模块引入了哪些代码,进行有目的性的优化代码

在打包环境中加,使用命令npm run build --report

if (process.env.npm_config_report) {
  config.plugins.push(new BundleAnalyzerPlugin())
}

如何基于vue-cli3.0构建功能完善的移动端架子

代码地址 项目地址: vue-cli3-H5

demo地址: https://zhouyupeng.github.io/vuecli3H5/#/

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
页面回到顶部的三种实现(锚标记,js)
Oct 01 Javascript
javascript控制Div层透明属性由浅变深由深变浅逐渐显示
Nov 12 Javascript
多个$(document).ready()的执行顺序实例分析
Jul 26 Javascript
jQuery拖拽插件gridster使用指南
Apr 21 Javascript
浅析Node.js:DNS模块的使用
Nov 23 Javascript
JavaScript用构造函数如何获取变量的类型名
Dec 23 Javascript
微信小程序登录换取token的教程
May 31 Javascript
angular 实现的输入框数字千分位及保留几位小数点功能示例
Jun 19 Javascript
详解vue服务端渲染浏览器端缓存(keep-alive)
Oct 12 Javascript
对Layer UI 模块化的用法详解
Sep 26 Javascript
jQuery弹框插件使用方法详解
May 26 jQuery
JavaScript中关于预编译、作用域链和闭包的理解
Mar 31 Javascript
解决vue 单文件组件中样式加载问题
Apr 24 #Javascript
vue router 用户登陆功能的实例代码
Apr 24 #Javascript
10个最受欢迎的 JavaScript框架(推荐)
Apr 24 #Javascript
vue.js多页面开发环境搭建过程
Apr 24 #Javascript
小程序实现订单倒计时功能
Apr 23 #Javascript
vue实现密码显示与隐藏按钮的自定义组件功能
Apr 23 #Javascript
使用Vue中 v-for循环列表控制按钮隐藏显示功能
Apr 23 #Javascript
You might like
php环境下利用session防止页面重复刷新的具体实现
2014/01/09 PHP
PHP和javascript常用正则表达式及用法实例
2014/07/01 PHP
Zend Framework创建自己的动作助手详解
2016/03/05 PHP
将PHP的session数据存储到数据库中的代码实例
2016/06/24 PHP
php注册审核重点解析(数据访问)
2017/05/23 PHP
thinkPHP微信分享接口JSSDK用法实例
2017/07/07 PHP
2017年最好用的9个php开发工具推荐(超好用)
2017/10/23 PHP
asp javascript 实现关闭窗口时保存数据的办法
2007/11/24 Javascript
javascript 面向对象全新理练之数据的封装
2009/12/03 Javascript
js页面跳转常用的几种方式
2010/11/25 Javascript
jquery简单的拖动效果实现原理及示例
2013/07/26 Javascript
Javascript中3个需要注意的运算符
2015/04/02 Javascript
纯jquery实现模仿淘宝购物车结算
2015/08/20 Javascript
jquery背景跟随鼠标滑动导航
2015/11/20 Javascript
jQuery手指滑动轮播效果
2016/12/22 Javascript
JavaScript计时器用法分析【setTimeout和clearTimeout】
2017/01/18 Javascript
Web技术实现移动监测的介绍
2017/09/18 Javascript
解决Vue使用swiper动态加载数据,动态轮播数据显示白屏的问题
2018/09/27 Javascript
详解Vue 项目中的几个实用组件(ts)
2019/10/29 Javascript
[58:11]守擂赛第二周擂主赛 DeMonsTer vs Leopard
2020/04/28 DOTA
Python使用装饰器模拟用户登陆验证功能示例
2018/08/24 Python
强悍的Python读取大文件的解决方案
2019/02/16 Python
Django 表单模型选择框如何使用分组
2019/05/16 Python
python 一个figure上显示多个图像的实例
2019/07/08 Python
Python人工智能之路 jieba gensim 最好别分家之最简单的相似度实现
2019/08/13 Python
详解Python图像处理库Pillow常用使用方法
2019/09/02 Python
Python作用域与名字空间原理详解
2020/03/21 Python
keras 两种训练模型方式详解fit和fit_generator(节省内存)
2020/07/03 Python
EJB的激活机制
2013/10/25 面试题
销售行业个人求职自荐信
2013/09/25 职场文书
毕业生动漫设计求职信
2013/10/11 职场文书
党员年度个人总结
2015/02/14 职场文书
简爱读书笔记
2015/06/26 职场文书
2019年描写人生经典诗句大全
2019/07/08 职场文书
2019朋友新婚祝福语精选
2019/10/10 职场文书
PHP遍历数组的6种方式总结
2021/11/17 PHP