本文讲述了毕业实习和正式工做1年以来,使用Vue开发项目的一些我的经历和想法,仅是我的总结,若有不合理的地方,欢迎吐槽。如下是本文的大概内容。 javascript
一开始实习接触Vue的脚手架是VueCli2版本,学习的webpack配置也都Cli2的,后来公司使用的是Cli3,因此有一个学习和适应的过程。
VueCli2和VueCli3的差异大概体如今:css
3.0:vue create。
2.0:vue init webpackhtml
3.0启动npm run serve
2.0启动npm run dev前端
2.0 config、build文件夹中进行项目的webpack、多环境和打包等配置
3.0 项目结构比2.0要简洁,缺乏了build和confilg文件,可自行建立与package.json同级的 vue.config.js 文件,进行配置。
主要的经常使用配置整理以下:vue
// vue.config.js 基本配置方法
module.exports = {
// 项目部署的基础路径
// 咱们默认假设你的应用将会部署在域名的根部,
// 好比 https://www.my-app.com/
// 若是你的应用时部署在一个子路径下,那么你须要在这里
// 指定子路径。好比,若是你的应用部署在
// https://www.foobar.com/my-app/
// 那么将这个值改成 `/my-app/`
// 基本路径 baseURL已通过时
publicPath: './',
// 打包项目时构建的文件目录,用法与webpack自己的output.path一致
outputDir: 'dist',
// 静态资源目录 (js, css, img, fonts)
assetsDir: 'assets',
// eslint-loader 是否在保存的时候检查,编译不规范时,设为true在命令行中警告,若设为error则不只警告,而且编译失败
lintOnSave: true,
// 调整内部的 webpack 配置。查阅 https://github.com/vuejs/vue-docs-zh-cn/blob/master/vue-cli/webpack.md
chainWebpack: () => {},
configureWebpack: () => {},
// vue-loader 配置项 https://vue-loader.vuejs.org/en/options.html
vueLoader: {},
// 生产环境是否生成 sourceMap 文件,默认true,若不须要生产环境的sourceMap,能够设置为false,加速生产环境的构建
productionSourceMap: true,
// css相关配置
css: {
// 是否使用css分离插件 ExtractTextPlugin,采用独立样式文件载入,不采用<style>方式内联至html文件中
extract: true,
// 是否在构建样式地图,false将提升构建速度
sourceMap: false,
// css预设器配置项
loaderOptions: {},
// 启用 CSS modules for all css / pre-processor files.
// 这个选项不会影响 `*.vue` 文件
modules: false
},
// 在生产环境下为 Babel 和 TypeScript 使用 `thread-loader`
// 在多核机器下会默认开启。
parallel: require('os').cpus().length > 1,
// 是否启用dll See https://github.com/vuejs/vue-cli/blob/dev/docs/cli-service.md#dll-mode
dll: false,
// PWA 插件相关配置 see https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-pwa
pwa: {},
// webpack-dev-server 相关配置
devServer: {
open: process.platform === 'darwin',
host: '0.0.0.0',//若是是真机测试,就使用这个IP
port: 1234,
https: false,
hotOnly: false,
proxy: null, // 设置代理
// proxy: {
// '/api': {
// target: '<url>',
// ws: true,
// changOrigin: true
// }
// },
before: app => {}
},
// 第三方插件配置
pluginOptions: {
// ...
}
}
复制代码
axios二次封装的目的主要是三个方面:java
在进行接口请求拦截进行配置处理的时候,针对如下参数,能够灵活配置。
node
参数 | 意义 | 例子 |
---|---|---|
url | 用于请求的服务器 URL | url: '/user' |
method | 建立请求时使用的方法 | method: 'get' |
baseURL | 自动加在 url 前面,除非 url 是一个绝对 URL,经过设置一个 baseURL 便于为 axios 实例的方法传递相对 URL |
baseURL: 'some-domain.com/api/' |
transformRequest | 容许在向服务器发送前,修改请求数据 // 只能用在 'PUT', 'POST' 和 'PATCH' 这几个请求方法 // 后面数组中的函数必须返回一个字符串,或 ArrayBuffer,或 Stream |
transformRequest: [function (data) { // 对 data 进行任意转换处理 |
return data; }], |
||
headers | 即将被发送的自定义请求头 | headers: {'X-Requested-With': 'XMLHttpRequest'}, |
params | 即将与请求一块儿发送的 URL 参数 | params: { ID: 12345 }, |
paramsSerializer | 负责 params 序列化的函数(e.g. www.npmjs.com/package/qs, api.jquery.com/jquery.para…) |
paramsSerializer: function(params) { return Qs.stringify(params, {arrayFormat: 'brackets'}) } |
data | data 是做为请求主体被发送的数据只适用于这些请求方法 'PUT', 'POST', 和 'PATCH' 在没有设置 transformRequest 时,必须是如下类型之一:- string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams - 浏览器专属:FormData, File, Blob - Node 专属: Stream |
data: { firstName: 'Fred' } |
timeout | 指定请求超时的毫秒数(0 表示无超时时间) | timeout: 1000 |
adapter | 容许自定义处理请求,以使测试更轻松,返回一个 promise 并应用一个有效的响应 | adapter: function (config) { /* ... */ }, |
auth | 表示应该使用 HTTP 基础验证,并提供凭据,这将设置一个 Authorization 头,覆写掉现有的任意使用 headers 设置的自定义 Authorization 头 |
auth: { username: 'janedoe', password: 's00pers3cret' }, |
responseType | 服务器响应的数据类型,能够是 'arraybuffer', 'blob', 'document', 'json', 'text', 'stream' | responseType: 'json', // 默认的 |
xsrfCookieName | 用做 xsrf token 的值的cookie的名称 | xsrfCookieName: 'XSRF-TOKEN' |
xsrfHeaderName | 承载 xsrf token 的值的 HTTP 头的名称 | xsrfHeaderName: 'X-XSRF-TOKEN', // 默认的 |
onUploadProgress | 容许为上传处理进度事件 | onUploadProgress: function (progressEvent) { // 对原生进度事件的处理 }, |
onDownloadProgress | 容许为下载处理进度事件 | onDownloadProgress: function (progressEvent) { // 对原生进度事件的处理 }, |
maxContentLength | 定义容许的响应内容的最大尺寸 | maxContentLength: 2000 |
validateStatus | 定义对于给定的HTTP 响应状态码是 resolve 或 reject promise 。若是 validateStatus 返回 true (或者设置为 null 或 undefined ),promise 将被 resolve; 不然,promise 将被 rejecte |
validateStatus: function (status) { return status >= 200 && status < 300; // 默认的 }, |
maxRedirects | 定义在 node.js 中 follow 的最大重定向数目 | maxRedirects: 5, // 默认的 |
httpAgent | httpAgent 和 httpsAgent 分别在 node.js 中用于定义在执行 http 和 https 时使用的自定义代理。 |
httpAgent: new http.Agent({ keepAlive: true }), httpsAgent: new https.Agent({ keepAlive: true }), |
proxy | 定义代理服务器的主机名称和端口 | proxy: { host: '127.0.0.1', port: 9000, auth: : { username: 'mikeymike', password: 'rapunz3l' } }, |
cancelToken | 指定用于取消请求的 cancel token | cancelToken: new CancelToken(function (cancel) { }) |
在网速较慢的状况下,容易出现用户屡次点击而重复请求使得页面抖动的问题,用户体验很差,所以进行拦截重复请求的处理。思路是:
建立请求队列 ---->jquery
-----拦截处理------
标识即将发送的请求---->
判断即将发送的请求与队列中的请求是否相同---->
若相同则执行当前请求的取消方法,并从请求队列中删除---->
建立即将请求的取消方法,放入队列中
拦截处理webpack
request.interceptors.request.use(
config => {
// 拦截重复请求(即当前正在进行的相同请求)
const requestData = getRequestIdentify(config, true); // 标识请求
removePending(requestData, true);// 取消重复请求
config.cancelToken = new CancelToken((c) => { // 建立当前请求的取消方法
pending[requestData] = c;
});
return config;
}, error => {
return Promise.reject(error)
})
复制代码
标识请求ios
const getRequestIdentify = (config, isReuest = false) => {
let url = config.url;
if (isReuest) {
url = config.baseURL + config.url.substring(1, config.url.length);
}
return config.method === 'get' ? encodeURIComponent(url + JSON.stringify(config.params)) : encodeURIComponent(config.url + JSON.stringify(config.data));
};
复制代码
取消重复请求
const pending = {};
const CancelToken = axios.CancelToken;
const removePending = (key, isRequest = false) => {
if (pending[key] && isRequest) {
pending[key]('取消重复请求');
}
delete pending[key];
};
复制代码
接口响应的拦截主要是对接口返回的数据进行提取、封装使用,以及对请求异常进行统一配置处理。
request.interceptors.response.use(
response => {
const data = response.data || {};
return data;
},
error => {
const code = error.response.status;
if (code) {
let msg = '';
switch (code) {
case 400:
msg = '请求错误';
break;
case 401:
msg = '未受权,请登陆';
break;
case 403:
msg = '拒绝访问';
break;
case 404:
msg = `请求${error.response.config.url}出现404错误`;
break;
case 408:
msg = '请求超时';
break;
case 500:
msg = '服务器内部错误';
break;
case 501:
msg = '服务未实现';
break;
case 502:
msg = '网关错误';
break;
case 503:
msg = '服务不可用';
break;
case 504:
msg = '网关超时';
break;
case 505:
msg = 'HTTP版本不受支持';
break;
}
Message.error(msg);
}
return Promise.reject(error);
}
)
复制代码
单独封装接口请求方法, GET方法的参数为params,POST方法的参数为data。
// api.js
import request from '@/utils/request';
export function APIPostMethod(data) { // 自定义接口方法
return request({
url: '/url1',
method: 'post',
data
});
}
export function APIGetMethod(params) { // 自定义接口方法
return request({
url: '/url2',
method: 'get',
params
});
}
复制代码
在业务中调用API方法
import { APIGetMethod, APIPostMethod } from '@/utils/request';
const params = {}
APIGetMethod(params).then(res => {
//...
//对数据处理
})
复制代码
简单来讲,为了防止XSS和CSFR攻击,浏览器的同源策略限制带来了先后端分离开发时的跨域问题。即当请求与响应不在同一个协议+域名+端口下,就不会被浏览器容许。可是同源策略只是浏览器的一种策略,不是HTTP协议的一部分,所以服务端调用HTTP接口只是使用HTTP协议,而不会经过浏览器,更不会执行JS脚本,因此不会触发同源策略机制,不存在跨域问题。针对这个特色,能够从前端配置代理服务器入手。
尝试过两种解决跨域的方法:
1.Node.js中间件代理
在vue-cli中,里用node+webpack+webpack-dev-server代理接口跨域。
//vue.config.js
const config = {
// ...
devServer: {
hot: true,
open: true,
host: '127.0.0.1',
// host: '0.0.0.0',//若是是真机测试,就使用这个IP
port: 8899,
https: false,
hotOnly: false,
proxy: {
'/': {
target: 'http://xxx.xxx.xx.xx:xxxx/',
logLevel: 'debug',
ws: false,
changOrigin: true
}
}
}
}
module.exports = config;
复制代码
2.Nginx反向代理
经过nginx配置一个代理服务器,域名与本地域名A一致,但端口不一样,反向代理访问对方B域名下的接口。
#proxy服务器
server {
listen 8088;
server_name _;
client_max_body_size 50m;
location / {
root html/dist;
try_files $uri $uri/ /index.html;
index /index.html;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
}
location ^~/api {
proxy_pass http://xxx.xxx.xx.xx:xxxx/api;
proxy_redirect default;
}
}
复制代码
前端在启动项目的时候,须要把项目proxy代理对应的8088端口上(感受这种方式有点多余,可是同事用这种方式,咱不敢说,因此我的这边是用的第一种)。
Nginx的方式更适合用于线上部署解决跨域使用,开发环境下,使用vue-cli中的devserve既方便又快捷。
懒加载也叫延迟加载,使组件进行异步加载。目的是延迟非必要资源的加载,减小页面加载的时间,从而优化页面的性能。
export default new Router({
routes:[
{
path: '/test',
name: 'test',
//懒加载
component: resolve => require(['../page/test.vue'], resolve),
},
]
})
复制代码
在路由懒加载下,代码根据路由被拆分为不一样的代码块,在切换进入相应的路由时,才对对应的代码块进行加载,加载更加高效了。
components: {
UpdateModal: resolve => { require(['./UpdateNormalTaskModal'], resolve); }
},
复制代码
在路由懒加载的前提下,进行组件懒加载的对比实验。
未使用组件懒加载:
按需加载通常用于在使用第三方库的时候,为了不第三方库过大,而形成的对首屏加载带来的过大的压力。
以VantUI按需加载为例
npm i babel-plugin-import -D
module.exports = {
plugins: [
['import', {
libraryName: 'vant',
libraryDirectory: 'es',
style: true
}, 'vant']
],
presets: [
'@vue/app'
]
};
复制代码
import { Swipe, SwipeItem, ImagePreview } from 'vant';
export default {
components: {
vanSwipe: Swipe,
vanSwipeItem: SwipeItem,
}
}
复制代码
在vue的v-bind语法中使用到本地资源时,路径是相对本地文件夹的相对路径,打包时没法解析。
将图片资源放在static静态资源文件夹下,在使用src时,直接访问根目录下的资源
好比图片放在public目录下,路径直接写为'/img/case/gkjg/7.jpg'
在data中采用require的方式,将图片资源导入,而后使用imgUrl变量。
data(){
return {
imgUrl:require("../assets/test.png")
}
}
复制代码
<keep-alive></keep-alive>
包含的组件会被缓存下来,不进行再次渲染DOM,从而节省性能,切换内容时会出发activated和deactivated两个生命周期钩子函数,被缓存的组件会保留当前组件的状态。
利用router的meta字段
//...router.js
export default new Router({
routes: [
{
path: '/',
name: 'Hello',
component: Hello,
meta: {
keepAlive: false // 不须要缓存
}
},
{
path: '/page1',
name: 'Page1',
component: Page1,
meta: {
keepAlive: true // 须要被缓存
}
}
]
})
复制代码
<keep-alive>
<router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive"></router-view>
复制代码
<keep-alive include="test-keep-alive">
<!-- 将缓存name为test-keep-alive的组件 -->
<component></component>
</keep-alive>
<keep-alive include="a,b">
<!-- 将缓存name为a或者b的组件,结合动态组件使用 -->
<component :is="view"></component>
</keep-alive>
<!-- 使用正则表达式,需使用v-bind -->
<keep-alive :include="/a|b/">
<component :is="view"></component>
</keep-alive>
<keep-alive exclude="test-keep-alive">
<!-- 将不缓存name为test-keep-alive的组件 -->
<component></component>
</keep-alive>
复制代码
结合路由beforeRouteLeave(to, from, next)
的钩子,设置to.meta.keepAlive
来指定目的页面是否进行keepAlive。
export default {
data() {
return {};
},
methods: {},
beforeRouteLeave(to, from, next) {
// 设置下一个路由的 meta
to.meta.keepAlive = true; // 当前页面跳转到下一个页面时,让目的页面缓存,不刷新
next();
}
};
复制代码
恰当使用keep-alive,结合activated和deactivated两个钩子函数,将不须要更新的内容缓存下来,将须要更新的内容放在两个钩子中去处理,这样能够减小没必要要的http请求和DOM重复渲染,提高了很多性能