如何基于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 相关文章推荐
图片自动缩小 点击放大
Jul 07 Javascript
讨论javascript(一)工厂方式 js面象对象的定义方法
Dec 15 Javascript
javascript温习的一些笔记 基础常用知识小结
Jun 22 Javascript
了不起的node.js读书笔记之mongodb数据库交互
Dec 22 Javascript
Highcharts入门之简介
Aug 02 Javascript
JS获取当前使用的浏览器名字以及版本号实现方法
Aug 19 Javascript
jQuery弹出div层过2秒自动消失
Nov 29 Javascript
详解Angular的内置过滤器和自定义过滤器【推荐】
Dec 26 Javascript
canvas雪花效果核心代码分享
Feb 19 Javascript
layui.js实现的表单验证功能示例
Nov 15 Javascript
Vue.js 表单控件操作小结
Mar 29 Javascript
详解nvm管理多版本node踩坑
Jul 26 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
ajax完美实现两个网页 分页功能的实例代码
2013/04/16 PHP
php中sql注入漏洞示例 sql注入漏洞修复
2014/01/24 PHP
PHP实现下载断点续传的方法
2014/11/12 PHP
php缓冲输出实例分析
2015/01/05 PHP
php实现批量删除挂马文件及批量替换页面内容完整实例
2016/07/08 PHP
PHP实现可精确验证身份证号码的工具类示例
2018/05/31 PHP
PHP+ajax实现二级联动菜单功能示例
2018/08/10 PHP
大家未必知道的Js技巧收藏
2008/04/07 Javascript
js实现键盘操作实现div的移动或改变的原理及代码
2014/06/23 Javascript
jquery+CSS3实现淘宝移动网页菜单效果
2015/08/31 Javascript
浅析jQuery移动开发中内联按钮和分组按钮的编写
2015/12/04 Javascript
前端框架Vue.js中Directive知识详解
2016/09/12 Javascript
浅谈bootstrap使用中的一些问题以及解决过程
2016/10/18 Javascript
js 颜色选择插件
2017/01/23 Javascript
javascript填充默认头像方法
2018/02/22 Javascript
vue的toast弹窗组件实例详解
2018/05/14 Javascript
深入浅析JS中的严格模式
2018/06/04 Javascript
vue+element 实现商城主题开发的示例代码
2020/03/26 Javascript
vue项目中使用bpmn-自定义platter的示例代码
2020/05/11 Javascript
OpenLayers3实现对地图的基本操作
2020/09/28 Javascript
JavaScript实现网页留言板功能
2020/11/23 Javascript
[51:26]VP vs VG 2018国际邀请赛小组赛BO2 第二场 8.19
2018/08/21 DOTA
python实现从字符串中找出字符1的位置以及个数的方法
2014/08/25 Python
简单谈谈Python中函数的可变参数
2016/09/02 Python
Python通过命令开启http.server服务器的方法
2017/11/04 Python
使用Python微信库itchat获得好友和群组已撤回的消息
2018/06/24 Python
基于TensorFlow常量、序列以及随机值生成实例
2020/01/04 Python
python实现坦克大战
2020/04/24 Python
python 带时区的日期格式化操作
2020/10/23 Python
Python中全局变量和局部变量的理解与区别
2021/02/07 Python
python中Pexpect的工作流程实例讲解
2021/03/02 Python
adidas美国官网:adidas US
2016/09/21 全球购物
写出程序把一个链表中的接点顺序倒排
2014/04/28 面试题
平面设计岗位职责
2013/12/14 职场文书
党员个人整改措施
2014/10/24 职场文书
Java实现注册登录跳转
2022/06/16 Java/Android