基于vue脚手架的一些配置及优化

css

  1. 私有样式写在<style lang="scss" scoped>里面,一定要加scoped

  2. 覆盖element的样式写在styles里,在common.scss里导入(注意:这里是所有页面统一用的样式),另外强烈建议不要直接覆盖ui框架里面的样式,请用一个class别名进行覆盖。

  3. css样式穿透:

    • 单个页面或组件覆盖element的样式,请写在<style lang="scss">里,并且以文件名添加class,防止影响其他样式,如
    文件名为 order.vue
    
    在<template>最外层div上加上class="order"
    需要覆盖的样式全写在.order里面
    <style lang="scss">
    .order {
      ...
    }
    </style>
    注:有同名文件的话把父文件带上,比如/a/order,/b/order,请用a-order作为class名
    
    • scss使用/deep/语法,css使用>>>语法:
    .table /deep/ .el-XXXX {
      ....
    }
    or
    .table >>> .el-XXXX {
      ....
    }
    补充
    .table ::v-deep .el-XXXX {
      ....
    }
    

    注:css用/deep/不会生效,scss用>>>不会生效。经过我的实验node-sass能解析/deep/和::v-deep,dart-sass不能解析/deep/,能解析::v-deep,由于vue-cli最新的全部用dart-sass,sass团队也从node-sass迁移至dart-sass,故建议用::v-deep
    参考vue-loader说明链接

  4. 样式统一风格,我们开发一个东西时,整体的ui风格一定要一致,不然会感觉很怪异。开发人员都是不同的,那么怎么做到统一风格呢?开发ui原则:

    • 能用ui框架做到的不要自己写,如<el-button>这个按钮,很多设计稿上是32px高度。但是直接写出来是40,这里我们很多同学就自己写样式把elment上的样式给覆盖了,但是<el-button>这个组件是可以通过设置size属性来改变大小的,自己强行写由于padding的不同反而样式不好看了,所以仔细看ui框架的文档还是很重要的。
    • 假如<el-button>这个按钮的高度在size属性里不能设置,如果只是几个按钮有这样的情况的话,建议在按钮所在的页面写样式覆盖,如果很多页面很常用的话,建议取一个特殊class名字,如btn-h32,在styles里全局引用。
  5. css的一些建议,css布局还是挺重要的,大多数前端同学css基础真的有些差,建议多看看css的相关知识,一个熟练的布局可以让代码结构,样式看起来清晰明了,添加需求时也非常方便。

项目结构

-src
  -api  // 请求文件夹 请求请全部写在本文件下,最好和发起请求的页面同路径同名,请求当有接口的注释说明
  -asset // 静态资源 图片等静态资源全部放在这个下面,最好区分路径,方便查找
  -commons // 公共样式 公共样式,这个主要是统一样式,进行ui框架等样式覆盖的,或者写的scss函数等,覆盖样式在main.js导入,
  -components // 公共组件
  -config // 项目配置
  -util // 工具函数
  -view // 视图
  -ruoter // 路由

axios:

  • axios请求的参数不要写在请求里面,应该调用时传参到请求里,因为后续参数增加后,只需要对调用点进行修改,不然的话要修改两个地方,而且在不知情的情况下只会修改调用点,会因为参数少而困扰很久。
export const xxxx = (id) => {
    return request({
        url: 'xxxx?id=' + id,  这里如果id换名了,或者添加参数了,就会很难看
        method: "get",
    });
}
xxxx(111) 调用  单从调用来看,不知道111是什么东西,id?
----------------------
export const xxxx = (params) => {
    return request({
        url: 'xxxx,
        method: "get",
        params
    });
}
xxxx({id: 1111})
  • 为什么要对axios进行封装?
  1. axios请求的post是用data,get是params,写的时候为了方便可以统一用data。

  2. 请求签名,你们以后可能会遇到。

  3. 配置不同环境请求(后面环境变量时一起说)。

export default function(options) {
    let params = {
        method: options.method,
        url: options.url,
        params: options.params,
        data: options.params
    }
    if (options.method.toLowerCase() === 'get') {
        delete params.data
    } else if (options.method.toLowerCase() === 'post') {
        delete params.params
    }
    return axios(params)
}

题外,进阶配置:
其实我们写的很多请求格式都一个样,可以集中处理,但是这样优缺点都有,优点是结构明确,代码少,缺点是不能用编辑器直接跳转引用的函数,自己取舍。

config与环境变量

confg主要用于环境区分,可以用作请求区分和功能区分。
环境变量主要是区分环境打包和配置环境变量参数。

环境变量

  1. 环境变量是什么?有什么用?

环境变量其实就是一个变量,vue-cli运行时会产生一个全局的变量,可以通过这个变量来区分环境和打包配置。

  1. 怎么配置环境变量

我们在打包是会运行对应的命令

开发环境 yarn(npm) run build:dev
测试环境 yarn(npm) run build:test
生产环境 yarn(npm) run build:prod
  • 这三个命令实际上是package.json下的scripts里面的命令,比如npm run build:dev实际上是运行的是vue-cli-service build --mode development

  • vue-cli-service build是vue-cli的打包命令,--mode development是设置一个模式,vue-cli会在打包时进行对根目录下的.env.xxx文件对查找,读取里面对变量并进行全局设置。比如development对应.env.development,prod对应.env.prod

看看里面有什么吧:

NODE_ENV=development
VUE_APP_TITLE=development
  • NODE_ENV,这个是打包生成文件的配置,developmentproduction的区别在于生成文件带不带hash值,一般来说打包带有hash值更好一点,所以一般除了本地环境都设置成production,这里有个注意点,npm run serve也是读取的.env.development,所以本地运行时不要把.env.development里面的NODE_ENV设置成production,不然修改后的热更新会非常慢。

  • VUE_APP_TITLE,这个就是我们所设置的环境变量了,在打包时我们可以通过console打印process.env.VUE_APP_TITLE发现这个就是development,所以能通过这个区分环境变量了。

  • 官方文档

config

简单解释一下

const env = process.env

let config = {
    host: "",    // 请求地址
    port: "",    // 端口号
    baseUrl: "", // 带不带baseUrl,比如/api,一般用作nginx代理匹配
    type: "development" // 什么环境
}

config.type = env.VUE_APP_TITLE

if (config.type === 'development') {
    config.baseUrl = '/api'
    // config.port = ':8084'
} else if (config.type === 'production') {
    config.host = 'xxx.xxx.xxx'
    config.port = ':8084'
    config.baseUrl = '/api'
} else if (config.type === 'test') {
    config.baseUrl = '/api'
}
export default config

使用,以axios为例

import cfg from '@/config'
axios.defaults.baseURL = `${cfg.host}${cfg.port}${cfg.baseUrl}`

production环境请求调用aaaa("/abc/efg")
那么我们请求的完整路径则是xxx.xxx.xxx:8084/api/abc/efg

注意事项:

  1. 若development的baseUrl设置了/api,且没设置host和port,vue.config.js的proxy也有/api,那么请求会被vue.config.js的proxy拦截代理到设置的target,简单说就是development的请求如果是完整的,那么不会被代理xx.xxx.xxx:8084/api/abc/efg,如果是/api/abc/efg,就会代理。

  2. 代理只会发生在本地运行,打包后是没有代理的,要代理的话前端是只设置/api的baseURl,用nginx做/api的匹配代理转发。

打包优化

打包优化的话有什么gzip压缩,移除console呀,这个自行百度吧,这里主要是cdn打包优化。
1. 为什么要用cdn,有什么好处?

其实我们写的代码引入的包在不升级的情况都是不变的,只有我们自己写的业务代码是变化的,所以每次打包没有必要把不变的代码也进行打包,这些包用cdn进行加载,可以走304缓存,让页面加载速度更快,打包体积更小。

  1. 当然业务代码也可以走cdn缓存,因为打包是带有hash值的,代码变化了hash也会变,但是入口的html千万不要缓存,因为一切加载的js,css都是html加载的,html被缓存了的话,js,css再怎么变也没效果。

  2. 免费cdn:

    • 首先,我们要明确那些包是需要抽离的,把要抽离的用一个对象装起来:
    const externals = {
    'vue': 'Vue',
    'vue-router': 'VueRouter',
    'vuex': 'Vuex',
    'axios': 'axios',
    'vue-baidu-map': 'VueBaiduMap',
    'nprogress': 'NProgress',
    'ant-design-vue': 'antd',
    'vue-cropper': 'vue-cropper'
    }

这里要注意,对象的key是包的名字,对象的value是包导出的名字,比如Vue也就是window.Vue可以直接调用,不是在main.js里import的xxx

  • 在chainWebpack里添加config.externals(externals),当然可以用环境变量做判断,到底是什么环境要添加cdn,config.externals是在打包时不会把这些打进包里,调用用对象的value代替。

  • 写个cdn的json,注意包的顺序,需要依赖的在下面,比如vue-router依赖于vue,所以vue要在vue-router上面,在chainWebpack里添加html要引入的链接

    const cdn = {
        dev: {
            css: [
                'https://cdn.jsdelivr.net/npm/ant-design-vue@1.3.13/dist/antd.min.css',
                'https://cdn.bootcss.com/nprogress/0.2.0/nprogress.min.css'
                ],
            js: [
                'https://cdn.jsdelivr.net/npm/vue-cropper@0.4.9/dist/index.min.js'
            ]
        },
        prod: {
            css: [
                'https://cdn.jsdelivr.net/npm/ant-design-vue@1.3.13/dist/antd.min.css',
                'https://cdn.bootcss.com/nprogress/0.2.0/nprogress.min.css'
            ],
            js: [
                'https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.min.js',
                'https://cdn.jsdelivr.net/npm/vue-router@3.0.7/dist/vue-router.min.js',
                'https://cdn.jsdelivr.net/npm/vuex@3.1.1/dist/vuex.min.js',
                'https://cdn.jsdelivr.net/npm/axios@0.19.0/dist/axios.min.js',
                'https://cdn.jsdelivr.net/npm/vue-baidu-map',
                'https://cdn.bootcss.com/nprogress/0.2.0/nprogress.min.js',
                'https://cdn.jsdelivr.net/npm/ant-design-vue@1.3.13/dist/antd.min.js',
                'https://cdn.jsdelivr.net/npm/vue-cropper@0.4.9/dist/index.min.js'
            ]
        }
    }

    config.plugin('html').tap(args => {
            if (~['analyz', 'production'].indexOf(process.env.VUE_APP_TITLE)) {
                args[0].cdn = cdn.prod
            } else {
                args[0].cdn = cdn.dev
            }
            return args
        })
  • 在html里用模版添加cdn的链接
    <head>
    ....
<!-- 使用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">
    <% } %>
    <!-- 使用CDN加速的JS文件,配置在vue.config.js下 -->
    <% for (var i in htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.js) { %>
    <link href="<%= htmlWebpackPlugin.options.cdn.js[i] %>" rel="preload" as="script">
    <% } %>
    ...
    </head>
    <body>
    <noscript>
      <strong>We're sorry but vue-test 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>
  1. 私有cdn的话,没有那么麻烦,把publicPath,设置成私有cdn的地址,这样html的引入全是cdn的链接,文件打包后把文件上传至cdn空间就行了。

项目地址

您可能还喜欢...

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注