首页 > 技术文章 > 项目总结

ginkgo-leaves 2019-04-22 18:19 原文

 

项目总结

app

技术栈

vue+vuex+vue-cli@3.0+stylus+axios+vant-ui


 

一些基本的配置

  • vue-cli3.0中简化了相应的配置文件,使得项目文件相对而言更加的清爽,配置也变得相对简单。在项目的目录文件下创建vue.config.js文件。常见配置如下:
  1. const path = require('path')
  2. module.exports = {
  3. // 基本路径
  4. publicPath: process.env.NODE_ENV === 'production'
  5. ? '/production-sub-path/'
  6. : '/',
  7. // 输出文件目录
  8. outputDir: process.env.NODE_ENV === 'production' ? 'dist' : 'devdist',
  9. // eslint-loader 是否在保存的时候检查
  10. lintOnSave: true,
  11. /**
  12. 1. webpack配置,see https://github.com/vuejs/vue-cli/blob/dev/docs/webpack.md
  13. **/
  14. chainWebpack: () => {
  15. },
  16. configureWebpack: config => {
  17. if (process.env.NODE_ENV === 'production') {
  18. // 为生产环境修改配置...
  19. } else {
  20. // 为开发环境修改配置...
  21. }
  22. config.resolve = { // 配置解析别名
  23. extensions: ['.js', '.vue'],
  24. alias: {
  25. '@': path.resolve(__dirname, './src'),
  26. 'public': path.resolve(__dirname, './public'),
  27. 'components': path.resolve(__dirname, './src/components'),
  28. 'common': path.resolve(__dirname, './src/common'),
  29. 'views': path.resolve(__dirname, './src/views'),
  30. 'store': path.resolve(__dirname, './src/store'),
  31. 'assets': path.resolve(__dirname, './src/assets'),
  32. 'api': path.resolve(__dirname, './src/api'),
  33. 'test': path.resolve(__dirname, './src/test')
  34. }
  35. }
  36. },
  37. // 生产环境是否生成 sourceMap 文件
  38. productionSourceMap: false,
  39. // css相关配置
  40. css: {
  41. // 是否使用css分离插件 ExtractTextPlugin
  42. extract: true,
  43. // 开启 CSS source maps?
  44. sourceMap: false,
  45. // css预设器配置项
  46. loaderOptions: {
  47. stylus: {
  48. // 全局导入stylus样式
  49. data: `@import "~@/common/stylus/index.styl";`
  50. }
  51. },
  52. // 启用 CSS modules for all css / pre-processor files.
  53. modules: true
  54. },
  55. // use thread-loader for babel & TS in production build
  56. // enabled by default if the machine has more than 1 cores
  57. parallel: require('os').cpus().length > 1,
  58. /**
  59. 2. PWA 插件相关配置,see https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-pwa
  60. */
  61. pwa: {},
  62. // webpack-dev-server 相关配置
  63. devServer: {
  64. open: false, // 编译完成是否打开网页
  65. host: '0.0.0.0', // 指定使用地址,默认localhost,0.0.0.0代表可以被外界访问
  66. port: 8080, // 访问端口
  67. https: false, // 编译失败时刷新页面
  68. hot: true, // 开启热加载
  69. hotOnly: false,
  70. proxy: null, // 设置代理
  71. overlay: { // 全屏模式下是否显示脚本错误
  72. warnings: true,
  73. errors: true
  74. },
  75. before: app => {
  76. }
  77. },
  78. /**
  79. 3. 第三方插件配置
  80. */
  81. pluginOptions: {}
  82. }

上面一段配置只适用于自己。


  • css在手机端的样式 
    根据个人习惯设置项目目录:个人会在项目的src下设置一个common目录,然后把所有的css样式都放到里面。重置文件一般为reset.less,全局样式文件一般为base.less,字体及颜色目录文件一般为variable.less,在项目中要使用到的一些特殊样式为mixin.styl,字体图表样式为icon.less,出口问文件为index.styl。具体参考内容如下:

index.styl文件

 
  1. @import "./base"
  2. @import "./reset"
  3. @import "./icon"
  4. @import "variable"

reset.styl

 
  1. /*移动端css样式重置,如果要修改全局样式请在base.styl文件里面修改*/
  2. html {
  3. font-family: "Helvetica Neue", Helvetica, STHeiTi, sans-serif;
  4. -ms-text-size-adjust: 100%;
  5. -webkit-text-size-adjust: 100%
  6. }
  7. html, body {
  8. -webkit-user-select: none;
  9. user-select: none;
  10. }
  11. html, body, div, object, iframe, applet,
  12. object, h1, h2, h3, h4, h5, h6, p, blockquote,
  13. pre, address, dl, dt, dd, ol, ul, li, table,
  14. caption, tbody, tfoot, thead, tr, th, td,
  15. article, aside, canvas, details, embed, figure,
  16. figcaption, footer, header, menu, nav, output, ruby,
  17. section, summary, time, mark, audio, video, progress {
  18. margin: 0;
  19. padding: 0;
  20. border: 0;
  21. vertical-align: baseline
  22. }
  23. a {
  24. text-decoration: none;
  25. -webkit-touch-callout: none;
  26. background-color: transparent
  27. }
  28. li {
  29. list-style: none
  30. }
  31. article, aside, details, figcaption, figure,
  32. footer, header, main, menu, nav,
  33. section, summary {
  34. display: block
  35. }
  36. audio, canvas, progress, video {
  37. display: inline-block
  38. }
  39. audio:not([controls]) {
  40. display: none;
  41. height: 0
  42. }
  43. [hidden], template {
  44. display: none
  45. }
  46. a:active, a:hover {
  47. outline: 0
  48. }
  49. abbr[title] {
  50. border-bottom: 1px dotted
  51. }
  52. b, strong {
  53. font-weight: bold
  54. }
  55. dfn {
  56. font-style: italic
  57. }
  58. h1 {
  59. font-size: 8.533vw;
  60. margin: 2.859vw 0
  61. }
  62. small {
  63. font-size: 80%
  64. }
  65. sub, sup {
  66. font-size: 75%;
  67. line-height: 0;
  68. position: relative;
  69. vertical-align: baseline
  70. }
  71. sup {
  72. top: -2.13vw;
  73. bottom: -2.13vw
  74. }
  75. sub {
  76. }
  77. img {
  78. border: 0;
  79. -webkit-touch-callout: none;
  80. }
  81. svg:not(:root) {
  82. overflow: hidden
  83. }
  84. figure {
  85. margin: 4.2667vw 10.667vw
  86. }
  87. hr {
  88. -moz-box-sizing: content-box;
  89. box-sizing: content-box;
  90. height: 0
  91. }
  92. pre {
  93. overflow: auto
  94. }
  95. code, kbd, pre, samp {
  96. font-family: monospace, monospace;
  97. font-size: 4.267vw
  98. }
  99. a, button, input, optgroup, select, textarea {
  100. -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
  101. }
  102. button, input, optgroup, select, textarea {
  103. color: inherit;
  104. font: inherit;
  105. margin: 0;
  106. -webkit-appearance: none;
  107. outline: none;
  108. line-height: normal
  109. }
  110. button {
  111. overflow: visible
  112. }
  113. button, select {
  114. text-transform: none
  115. }
  116. button, html input[type="button"], input[type="reset"], input[type="submit"] {
  117. -webkit-appearance: button;
  118. cursor: pointer
  119. }
  120. button[disabled], html input[disabled] {
  121. cursor: default
  122. }
  123. button::-moz-focus-inner, input::-moz-focus-inner {
  124. border: 0;
  125. padding: 0
  126. }
  127. input {
  128. line-height: normal
  129. }
  130. input[type="checkbox"], input[type="radio"] {
  131. box-sizing: border-box;
  132. padding: 0
  133. }
  134. input[type="number"]::-webkit-inner-spin-button,
  135. input[type="number"]::-webkit-outer-spin-button {
  136. height: auto
  137. }
  138. input[type="search"] {
  139. -webkit-appearance: textfield;
  140. -moz-box-sizing: content-box;
  141. -webkit-box-sizing: content-box;
  142. box-sizing: content-box
  143. }
  144. input[type="search"]::-webkit-search-cancel-button,
  145. input[type="search"]::-webkit-search-decoration {
  146. -webkit-appearance: none
  147. }
  148. fieldset {
  149. border: 1px solid silver;
  150. margin: 0 2px;
  151. padding: 1.493vw 2.667vw 3.2vw
  152. }
  153. legend {
  154. border: 0;
  155. padding: 0
  156. }
  157. textarea {
  158. overflow: auto
  159. }
  160. optgroup {
  161. font-weight: bold
  162. }
  163. table {
  164. border-collapse: collapse;
  165. border-spacing: 0
  166. }
  167. td, th {
  168. padding: 0
  169. }

base.styl文件

 
  1. // 整体样式
  2. body, html
  3. line-height: 1
  4. font-weight: 200
  5. font-family: 'PingFang SC', 'STHeitiSC-Light', 'Helvetica-Light', arial, sans-serif
  6. // 清除浮动
  7. .clearfix
  8. display: inline-block
  9. &:after
  10. display: block
  11. content: "."
  12. height: 0
  13. line-height: 0
  14. clear: both
  15. visibility: hidden
  16. // 图片处理1.5倍像素屏幕1px处理与2倍像素屏幕1px处理
  17. @media (-webkit-min-device-pixel-ratio: 1.5),(min-device-pixel-ratio: 1.5)
  18. .border-1px
  19. &::after
  20. -webkit-transform: scaleY(0.7)
  21. transform: scaleY(0.7)
  22. @media (-webkit-min-device-pixel-ratio: 2),(min-device-pixel-ratio: 2)
  23. .border-1px
  24. &::after
  25. -webkit-transform: scaleY(0.5)
  26. transform: scaleY(0.5)

variable.styl文件

 
  1. // 颜色定义规范
  2. $color-background = #222
  3. $color-background-d = rgba(0, 0, 0, 0.3)
  4. $color-highlight-background = #333
  5. $color-dialog-background = #666
  6. $color-theme = #ffcd32
  7. $color-theme-d = rgba(255, 205, 49, 0.5)
  8. $color-sub-theme = #d93f30
  9. $color-text = #fff
  10. $color-text-d = rgba(255, 255, 255, 0.3)
  11. $color-text-l = rgba(255, 255, 255, 0.5)
  12. $color-text-ll = rgba(255, 255, 255, 0.8)
  13. //字体定义规范
  14. $font-size-10 = 2.667vw
  15. $font-size-12 = 3.2vw
  16. $font-size-14 = 3.733vw
  17. $font-size-16 = 4.267vw
  18. $font-size-18 = 4.8vw
  19. $font-size-20 = 5.33vw
  20. $font-size-22 = 5.867vw

mixin.styl文件

 
  1. // 上边框1px
  2. border-1px($color)
  3. position: relative
  4. &:after
  5. display: block
  6. position: absolute
  7. left: 0
  8. bottom: 0
  9. width: 100%
  10. border-top: 1px solid $color
  11. content: ' '
  12. // 没有边框
  13. border-none()
  14. &:after
  15. display: none
  16. // 图片,3倍和2倍
  17. bg-image($url)
  18. background-image: url($url + "@2x.png")
  19. @media (-webkit-min-device-pixel-ratio: 3),(min-device-pixel-ratio: 3)
  20. background-image: url($url + "@3x.png")

值得说明的是字体图标,项目一般会使用iconfont字体图标


 

vuex的使用

  • 目录结构 
    由于很少使用到vuex模块,为了更加的熟悉vuex模块,在此记录:

    1. index.js 
      1. import Vue from 'vue'
      2. import Vuex from 'vuex'
      3. import * as actions from './actions'
      4. import * as getters from './getters'
      5. import mutations from './mutations'
      6. import states from './states'
      7. Vue.use(Vuex)
      8. export default new Vue.Store({
      9. actions,
      10. getters,
      11. state,
      12. mutations
      13. })

    2.state.js

    1. const state = {
    2. name:''
    3. }
    4. export default states

    3.mutation-types.js

    1. export const SET_NAME = set_name // 修改名字信息(请加上注释)

    4.mutations.js

    1. import * as type from './mutation-types'
    2. const mutations = {
    3. [type.SET_NAME](state,name){
    4. state.name = name
    5. }
    6. }
    7. export mutations

    5.getters.js

    1. export const name = state => state.name

    6.actions.js

    1. import * as type from './mutation-types'
    2. export const changeName = ({commit},{newName}){
    3. commit(types.SET_NAME, newName)
    4. }
 

 

axiox封装

 
axiox 的封装可以分为三步,个人喜欢在项目目录文件夹下建立一个api 文件来专门存放关于api 接口的一些东西
  • http.js 用来专门封装 axios 的接口设置,一般会和vue-axios使用

    http.js文件

 
  1. import axios from 'axios'
  2. import vueAxios from 'vue-axios'
  3. import Vue from 'vue'
  4. import QS from 'qs'
  5. Vue.use(vueAxios,axios)
  6. // axios 环境设置
  7. if(process.env.NODE_ENV === 'production'){
  8. axios.defaults.baseURL= 'http://prouction.com'
  9. }else if(process.env.NODE_ENV === 'development'){
  10. axios.defaults.baseURL = 'http://development.com'
  11. }else if(process.env.NODE_ENV === 'debug'){
  12. axios.defaults.baseURL = 'http://debug.com'
  13. }
  14. // axios请求超时
  15. axios.defaults.timeout = 10000
  16. // post请求头
  17. axios.defaults.headers = {
  18. 'X-Requested-With': 'XMLHttpRequest',
  19. 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
  20. }
  21. // 请求拦截器
  22. axios.interceptors.request.use(
  23. config=>{
  24. // 每次发送请求之前判断是否存在token,如果存在,则统一在http请求的header都加上token,不用每次请求都手动添加了
  25. // 即使本地存在token,也有可能token是过期的,所以在响应拦截器中要对返回状态进行判断
  26. // const token = store.state.user.token
  27. // token && (config.headers.Authorization = `Bearer ${token}`)
  28. config.data = QS.stringify(config.data)
  29. return config
  30. },
  31. error => {
  32. return Promise.error(error)
  33. })
  34. // 相应拦截器
  35. let errorHandle = (status, other) => {
  36. if (status) {
  37. switch (status) {
  38. /**
  39. * 401: 未登录
  40. * 未登录则跳转登录页面,并携带当前页面的路径
  41. *在登录成功后返回当前页面,这一步需要在登录页操作。
  42. */
  43. case 401:
  44. // tip('未登录,请先登录')
  45. break
  46. /**
  47. * 403 token过期,登录过期对用户进行提示,清除本地token和清空vuex中token对象,跳转登录页面
  48. */
  49. case 403:
  50. // tip('登录过期,请重新登录')
  51. // setTimeout(() => {
  52. // logout()
  53. // }, 1000)
  54. break
  55. // 404请求不存在
  56. case 404:
  57. // tip('请求的资源不存在')
  58. break
  59. // 其他错误,直接抛出错误提示
  60. default:
  61. // tip({
  62. // message: error.response.data.message,
  63. // duration: 1500, // 持续时间
  64. // forbidClick: true
  65. // })
  66. console.log(other)
  67. break
  68. }
  69. }
  70. }
  71. axios.interceptors.response.use(
  72. // 拿到数据200
  73. res => res.status === 200 ? Promise.resolve(res) : Promise.reject(res),
  74. // 未拿到,非200
  75. error => {
  76. if (error.response === undefined) {
  77. console.log('response:' + error.message)
  78. return Promise.reject(error)
  79. // tip('后台错误,请求undfined')
  80. }
  81. errorHandle(error.response.status, error.response.data.message)
  82. return Promise.reject(error.response)
  83. }
  84. )
  85. /**
  86. * 封装接口调用方法
  87. * @param url:地址
  88. * @param data:传入参数
  89. * @param type:类型(仅限于get/post)
  90. * @returns {Promise<any>}
  91. * @constructor
  92. */
  93. export function Axios (url, data = {}, type = 'GET') {
  94. return new Promise((resolve, reject) => {
  95. // 执行异步请求
  96. let promise
  97. if (type === 'GET') {
  98. // 准备url的query参数数据
  99. let dataStr = '' // 数据拼接字符串
  100. Object.keys(data).forEach(key => {
  101. dataStr += key + '=' + data[key] + '&'
  102. })
  103. if (dataStr !== '') {
  104. dataStr = dataStr.substring(0, dataStr.lastIndexOf('&'))
  105. url = url + '?' + dataStr
  106. }
  107. // get请求
  108. promise = axios.get(url)
  109. } else {
  110. // 发送post请求
  111. promise = axios.post(url, data)
  112. }
  113. promise.then(response => {
  114. // 成功拿到response的data
  115. resolve(response.data)
  116. }).catch(error => {
  117. reject(console.log(error))
  118. })
  119. })
  120. }
  • base.js 里面存放的是api接口模块,所有的接口都存放在里面:

base.js文件

 
  1. let baseURL = '10.10.21.86'
  2. export nameURL = baseURL + '/name'
  3. export let loginURL = baseURL + '/account/login'
  4. export let guestURL = baseURL + '/account/guest'
  5. export let regURL = baseURL + '/account/reg'
  6. export let accountURL = baseURL + '/home/account'
  7. export let kefuURL = baseURL + '/home/getkefu'

api.js 文件是获取数据的接口方法模块:

api.js文件

 
  1. import {Axios} from './http'
  2. import * as url from './base'
  3. esport const getName = ({data})=>Axios(url.nameURL,{data},'POST')

 vuex 的 actions.js 文件里面可以直接调用方法从而异步获取数据的

 

推荐阅读