项目开发
.env(在所有的环境中被载入)
# 环境
NODE_ENV = "development"
.env.staging(只在测试环境中被载入)
# staging 预演环境,即线上的dev环境
NODE_ENV = "production"
# publicPath
VUE_APP_PUBLIC_PATH = '/'
.env.production(只在生产环境中被载入)
# 环境
NODE_ENV = "production"
# publicPath
# VUE_APP_PUBLIC_PATH = 'https://cdn.jiaoyihu.com/'
# VUE_APP_PUBLIC_PATH = '/'
VUE_APP_PUBLIC_PATH = 'http://h5.promotion.jiaoyihu.com'
public-path.js
const publicPath = process.env.VUE_APP_PUBLIC_PATH
module.exports = publicPath
subdirectory-path.js
const time = new Date().getTime()
const serveName = 'promotion-h5'
const subdir = serveName + '/' + time
module.exports = subdir
vue.config.js
module.exports = {
// 为项目中的所有资源指定一个基础路径,它被称为公共路径(publicPath)
publicPath: isProduction ? publicpath : '/',
// 构建文件的目录
outputDir: 'dist',
// 生成静态资源的目录
assetsDir: subdirectoryPath
}
线上静态资源路径:http://h5.promotion.jiaoyihu.com/promotion-h5/1610172249953/img/ic_home_daishou@2x.6ce160ad.png
本地静态资源路径:/promotion-h5/1610077464291/img/ic_home_daishou@2x.6ce160ad.png
创建 axios 实例
可以使用自定义配置新建一个 axios 实例
axios.create([config])
const instance = axios.create({
// 设置超时时间30秒
timeout: 30000,
// 请求的baseUrl
baseURL: baseUrl,
// 请求头部信息
headers: {
'Content-Type': 'application/json'
},
// 修改请求数据
transformRequest: [
function (data, headers) {
return JSON.stringify(data)
}
],
// 跨域请求时允许携带凭证(cookie)
withCredentials: process.env.NODE_ENV === 'production'
})
全局的 axios 默认值
axios.defaults.baseURL = 'https://api.example.com'
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'
data和contentType配合使用:
contentType: "application/x-www-form-urlencoded"
// 对应的data类型
data: "name='张三'&age=20"
contentType: "application/json"
// 对应的data类型
data: JSON.stringify({name: "张三",age: 20})即data: {"name": "张三","age": 20}
例子:
data: {
name: '张三',
age: 18
}
// 修改请求数据
transformRequest: [function (data, headers) {
let ret = ''
for (let it in data) {
// 去除空字符串的请求字段
if (data[it] !== '' && data[it] !== undefined) {
if (ret !== '') ret += '&'
ret += encodeURIComponent(it) + '=' + encodeURIComponent(data[it])
}
}
return ret // "name='张三'&age=18"
}]
Vue项目中axios的二次封装
为什么要二次封装???axios不是对ajax封装了一次吗???
api统一管理,不管接口有多少,所有的接口都可以非常清晰,易于项目后期的维护以及迭代
config.js
const prodConfigs = {
// 开发一套,推dev1分支
'promotion-h5.dev1.fe.sdh-dev.com': {
apiHost: 'http://promotion-oms.dev1.server.sdh-dev.com',
vconsole: false
},
// 测试一套
'promotion-h5.test1.fe.sdh-test.com': {
apiHost: 'http://promotion-oms.test1.server.sdh-test.com',
vconsole: false
},
// 预发还没配
'www-pre.zjyushi.com': {
apiHost: 'https://zjyushi-oms-server-pre.youximao.com',
vconsole: false
},
// 正式还没配
'www.zjyushi.com': {
apiHost: 'https://zjyushi-oms-server.youximao.com',
vconsole: false
}
}
// 线上环境默认的配置
const defaultConfig = {
// apiHost: 'http://zjyushi-oms-server.youximao.com',
apiHost: 'http://promotion-server.promotion.jiaoyihu.com',
vconsole: false
}
// 本地环境配置
const localConfig = {
apiHost: 'http://yapi.youximao.cn/mock/823',
vconsole: false
}
let hostConfig = prodConfigs[location.hostname] || defaultConfig
if (process.env.NODE_ENV === 'development') hostConfig = localConfig
export default hostConfig
setup.js
import axios from 'axios'
import hostConfig from '@apis/config.js'
const baseUrl = hostConfig.apiHost
// 正式环境 -- 请使用真实请求 -- start
const instance = axios.create({
// 设置超时时间30秒
timeout: 30000,
// 请求的baseUrl
baseURL: baseUrl,
// 请求头部信息
headers: {
'Content-Type': 'application/json'
},
// 修改请求数据
transformRequest: [
function (data, headers) {
// 对传参 data 进行任意转换处理
// let ret = ''
// for (const it in data) {
// if (ret !== '') ret += '&'
// if (Object.prototype.toString.call(data[it]) === '[object Array]' && !data[it].length) {
// ret += encodeURIComponent(it) + '=' + '[]'
// } else {
// ret += encodeURIComponent(it) + '=' + encodeURIComponent(data[it])
// }
// }
return JSON.stringify(data)
}
],
// 跨域请求时允许携带凭证(cookie)
withCredentials: process.env.NODE_ENV === 'production'
})
// 响应拦截器
instance.interceptors.response.use(
res => {
return res.data
},
error => {
return Promise.reject(error)
}
)
// 前端阻止重复请求的最核心的方法是CanelToken
const CancelToken = axios.CancelToken
function addCancel (config, $this, cancel) {
if ($this) {
config.CancelToken = new CancelToken(function executor (c) {
$this[cancel] = c
})
}
}
function createAPI (url, method, data, $this, cancel) {
let config = {}
if (method === 'post' || method === 'POST') {
config = {
method: 'post',
url: url,
data
}
} else {
config = {
method: 'post',
url: url,
params: data
}
}
addCancel(config, $this, cancel)
return instance(config)
}
// 正式环境 -- 请使用真实请求 -- end
export default createAPI
公共响应参数
参数 | 类型 | 是否必填 | 最大长度 | 描述 | 示例值 |
---|---|---|---|---|---|
code | String | 是 | 4 | 见协议状态码定义 | 2000 |
message | String | 是 | 32 | 状态码描述,code 码状态为成功状态时,可不返回些字段 | success |
data | String | 是 | 19 | 数组、对象、布尔值等。为空时返回空数组或者空对象 |
code码
code码 | 状态信息 | 操作 |
---|---|---|
2000 | 成功 | |
2001 | 未登录 | 统一跳转登录页面 |
2002 | 权限不足 | |
2003 | 不在白名单内 | |
2004 | 处理异常 |
CSS @import用法
在Vue单文件组件中使用( ;
必须要有)
<style lang="less" scoped>
@import "./home.less";
</style>
Vue中的 @
Vue项目中默认定义了 @ 和 vue$ 两个别名,@就代表着到src这个文件夹的路径
在vue.config.js中配置自定义路径别名
const path = require('path')
function resolve(dir) {
return path.join(__dirname, dir)
}
module.exports = {
lintOnSave: true,
chainWebpack: (config) => {
config.resolve.alias
.set('@', resolve('src'))
.set('@assets',resolve('src/assets'))
}
}
Vue中的 ~
~ 是 stylus-loader 的东西,~ 是相对于其他路径(文件)的
~@/assets/scss/_variables.scss
表示相对于 @ 下的 assets/scss/_variables.scss
Vue使用import…from…来导入组件,库,变量等
extensions指定了from后可导入的文件类型,上面定义的这3类可导入文件,js和vue是可以省略后缀的,json不可以省略后缀
import test from './test.vue'
等同于 import test from './test'
import test from './test.js'
等同于 import test from './test'
若test.js,test.vue同时存在于同一个文件夹下,则import的导入优先级是:js > vue
from后的来源除了文件,还可以是文件夹:import test from './components'
该情况下的逻辑是:
if(package.json存在 && package.main字段存在 && package.main指定的js存在) {
取package.main指定的js作为from的来源,即使该js可能格式或内容错误
} else if(index.js存在){
取index.js作为from的来源
} else {
取index.vue作为from的来源
}
因此若from的来源是文件夹,那么在package.json存在且设置正确的情况下,会默认加载package.json;若不满足,则加载index.js;若不满足,则加载index.vue
注意加载文件夹的形式与上面省略后缀的形式是完全相同的,所以一个省略后缀的from来源有可能是 .vue
.js
或者文件夹
环境变量和模式
下列文件来指定环境变量:
.env # 在所有的环境中被载入
.env.local # 在所有的环境中被载入,但会被 git 忽略
.env.[mode] # 只在指定的模式中被载入
.env.[mode].local # 只在指定的模式中被载入,但会被 git 忽略
如 .env 文件:
# 环境
NODE_ENV = "development"
被载入的环境变量将会对 vue-cli-service
的所有命令、插件和依赖可用
环境加载属性
为一个特定模式准备的环境文件(例如.env.production文件)将会比一般的环境文件(例如.env)拥有更高的优先级,此外Vue CLI 启动时已经存在的环境变量拥有最高优先级,并不会被 .env 文件覆写
模式
模式是Vue CLI项目中一个重要的概念,默认情况下一个 Vue CLI 项目有三个模式:
development
模式用于vue-cli-service serve
production
模式用于vue-cli-service build
和vue-cli-service test:e2e
test
模式用于vue-cli-service test:unit
注意模式不同于 NODE_ENV
,一个模式可以包含多个环境变量
你可以通过传递 --mode
选项参数为命令行覆写默认的模式,如果你想要在构建命令中使用开发环境变量,请在你的 package.json
脚本中加入
"dev-build": "vue-cli-service build --mode development"
process.env.NODE_ENV
process对象是全局变量,它提供当前 node.js 的有关信息,以及控制当前 node.js 的有关进程。因为是全局变量,它对于node应用程序是始终可用的,无需require()。process是一个对象,env是它的一个属性,这个属性返回包含用户环境信息的对象。在终端输入node后,在输入process.env可以看到打印出来的信息
NODE_ENV不是process.env对象上原有的属性,它是我们自己添加上去的一个环境变量,用来确定当前所处的阶段。一般生产阶段设为production,开发阶段设为develop,然后在脚本中读取process.env.NODE_ENV
项目优化
生成打包报告
打包时,为了直观地发现项目中存在的问题,可以在打包时生成报告,生成报告的方式有两种:
-
在package.json中配置的方式,生成report.html以帮助分析打包报告
"scripts": { "build": "vue-cli-service build --report" }
-
在可视化的Vue CLI面板的控制台和分析直接查看报告
通过externals加载外部CDN资源
默认情况下,通过import语法导入的第三方依赖包,最终会被打包合并到同一个文件(chunk-vendors.js)中,从而导致打包成功后,单文件体积过大的问题
解决方法:可通过webpack的externals节点,来配置并加载外部的CDN资源,凡是声明在externals中的第三方依赖包,都不会被打包
在vue.config.js中配置
module.exports = {
chainWebpack: config => {
//发布模式
config.when(process.env.NODE_ENV === 'production', config => {
config.entry('app').clear().add('./src/main-prod.js')
config.set('externals',{
vue: 'Vue',
'vue-router': 'VueRouter',
axios: axios,
echarts: 'echarts',
nprogress: 'NProgress'
})
})
}
}
然后需要在public/index.html文件的头部,添加如下的CDN资源引用:
<!-- nprogress的样式表文件 -->
<link href="https://cdn.bootcdn.net/ajax/libs/nprogress/0.2.0/nprogress.min.css" rel="stylesheet">
<script src="https://cdn.bootcdn.net/ajax/libs/vue/3.0.2/vue.cjs.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/vue-router/3.4.8/vue-router.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.0/axios.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/echarts/5.0.0-beta.2/echarts.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/nprogress/0.2.0/nprogress.min.js"></script>
Element-UI组件按需加载或通过CDN加载Element-UI
虽然启用了element-ui组件的按需加载,尽可能的减少了打包的体积,但是那些被按需加载的组件还是占用了较大的文件体积。此时可以将element-ui中的组件通过CDN形式来加载,能够进一步减小打包后的文件体积
在main-prod.js中,将element-ui按需加载的代码(*import* './plugins/element.js'
)删除
在public/index.html文件的头部,添加如下的CDN资源引用:
<!-- element-ui的样式表文件 -->
<link href="https://cdn.bootcdn.net/ajax/libs/element-ui/2.14.0/theme-chalk/index.css" rel="stylesheet">
<!-- element-ui的js文件 -->
<script src="https://cdn.bootcdn.net/ajax/libs/element-ui/2.14.0/index.js"></script>
路由懒加载
首页内容定制
只在发布(生产)阶段移除所有的console
babel.config.js是全局共享的配置文件,不管是项目开发阶段还是发布阶段 babel.config.js 配置文件都会生效
-
安装:
npm install babel-plugin-transform-remove-console --sav-dev
-
在babel.config.js中配置
const prodPlugins = [] if(process.env.NODE_ENV === 'production'){ prodPlugins.push('transform-remove-console') } module.exports = { plugins: [ //发布阶段需要用到的插件数组 ...prodPlugins ] }
通过vue.config.js修改Vue项目中webpack的默认配置
为开发模式和发布模式指定不同的打包入口
默认情况下,Vue项目的开发模式与发布模式共用同一个打包的入口文件。为了将项目的开发过程和发布过程分离,可以为两种模式,各自指定打包的入口文件
在vue.config.js中配置
module.exports = {
chainWebpack: config => {
//发布模式
config.when(process.env.NODE_ENV === 'production', config => {
config.entry('app').clear().add('./src/main-prod.js')
})
//开发模式
config.when(process.env.NODE_ENV === 'development', config => {
config.entry('app').clear().add('./src/main-dev.js')
})
},
configureWebpack: () => {}
}
chainWebpack和configureWebpack的作用相同,唯一的区别就是它们修改webpack配置的方式不同
- chainWebpack通过链式编程的形式来修改默认的webpack配置
- configureWebpack通过操作对象的形式来修改默认的webpack配置
项目上线相关配置
- 通过node创建web服务器
- 开启gzip配置
- 配置https服务
- 使用pm2管理应用