这篇文章总结了vue项目的所遇到的问题,包括跨域、用户认证、接口统一管理、路由配置、兼容性处理,性能优化等内容。javascript
项目github地址 :css
根据如下教程安装,而后设置好环境变量
http://www.runoob.com/nodejs/...html
视频教程 http://101.110.118.22/github....前端
centos若是装不上看这里:https://www.rosehosting.com/b...vue
npm默认使用的源的服务器在国外下载速度慢,因此须要更换源如下两种方法任选一种java
参考连接: https://npm.taobao.org/
# 安装 npm install -g cnpm --registry=https://registry.npm.taobao.org #安装完cnpm,以后再按照依赖就要使用cnpm cnpm install [包名]
参考连接 https://segmentfault.com/a/11...
修改源为淘宝的源node
npm config set registry http://registry.npm.taobao.org/
咱们在发布本身包的时候须要将官方的源改回来linux
npm config set registry https://registry.npmjs.org/
切换nodejs版本有两种方式,分别是nvm
和n
,n更简单推荐使用
参考连接 https://www.jianshu.com/p/c64...官网 https://github.com/tj/nwebpack
#安装 npm install -g n #使用n下载所需node版本 n 版本号 #下载最新版本 n latest # 切换版本 输入 n, 而后选中所需版本 #以指定的版原本执行版本 n use 7.4.0 index.js
linux使用n安装新版本nodejs以后,若是node -v
仍是原来的版本,那么就须要改变一下环境变量ios
vim .bash_profile
export NODE_HOME=/usr/local #NODE_HOME改为新版本nodejs安装的目录,若是找不到,find / -name node export PATH=$NODE_HOME/bin:$PATH export NODE_PATH=$NODE_HOME/lib/node_modules:$PATH
修改环境变量参考:https://blog.csdn.net/yi412/a...
参考文档 http://javascript.ruanyifeng....
vue-cli目前已经更新到3版本,vue-cli3把webpack相关的配置隐藏起来了,全部的配置都在vue.config.js文件夹中,因此使用vue-cli3须要的webpack水平较高,建议使用vue-cli2
参考连接:https://github.com/vuejs/vue-...
安装:
npm install -g vue-cli
用法:
$ vue init < template-name > < project-name >
例:
$ vue init webpack my-project
目前可用的模块包括:
vue-cli3x的官方文档:https://cli.vuejs.org/
Vue-cli3 中vue.config.js文件配置参考文档:https://cli.vuejs.org/zh/conf...
Vue CLI 的包名称由 vue-cli
改为了 @vue/cli
。 若是你已经全局安装了旧版本的 vue-cli
(1.x 或 2.x),你须要先经过 npm uninstall vue-cli -g
或 yarn global remove vue-cli
卸载它。
安装
npm install -g @vue/cli
安装了vue-cli3若是还想使用vue-cli2的init功能,须要安装一个桥接功能
npm install -g @vue/cli-init
// vue.config.js 配置说明 //官方vue.config.js 参考文档 https://cli.vuejs.org/zh/config/#css-loaderoptions // 这里只列一部分,具体配置参考文档 module.exports = { // 部署生产环境和开发环境下的URL。 // 默认状况下,Vue CLI 会假设你的应用是被部署在一个域名的根路径上 //例如 https://www.my-app.com/。若是应用被部署在一个子路径上,你就须要用这个选项指定这个子路径。例如,若是你的应用被部署在 https://www.my-app.com/my-app/,则设置 baseUrl 为 /my-app/。 baseUrl: process.env.NODE_ENV === "production" ? "./" : "/", // outputDir: 在npm run build 或 yarn build 时 ,生成文件的目录名称(要和baseUrl的生产环境路径一致) outputDir: "dist", //用于放置生成的静态资源 (js、css、img、fonts) 的;(项目打包以后,静态资源会放在这个文件夹下) assetsDir: "assets", //指定生成的 index.html 的输出路径 (打包以后,改变系统默认的index.html的文件名) // indexPath: "myIndex.html", //默认状况下,生成的静态资源在它们的文件名中包含了 hash 以便更好的控制缓存。你能够经过将这个选项设为 false 来关闭文件名哈希。(false的时候就是让原来的文件名不改变) filenameHashing: false, // lintOnSave:{ type:Boolean default:true } 问你是否使用eslint `lintOnSave`: true, //若是你想要在生产构建时禁用 eslint-loader,你能够用以下配置 // lintOnSave: process.env.NODE_ENV !== 'production', //是否使用包含运行时编译器的 Vue 构建版本。设置为 true 后你就能够在 Vue 组件中使用 template 选项了,可是这会让你的应用额外增长 10kb 左右。(默认false) // runtimeCompiler: false, /** * 若是你不须要生产环境的 source map,能够将其设置为 false 以加速生产环境构建。 * 打包以后发现map文件过大,项目文件体积很大,设置为false就能够不输出map文件 * map文件的做用在于:项目打包后,代码都是通过压缩加密的,若是运行时报错,输出的错误信息没法准确得知是哪里的代码报错。 * 有了map就能够像未加密的代码同样,准确的输出是哪一行哪一列有错。 * */ productionSourceMap: false, // 它支持webPack-dev-server的全部选项 devServer: { host: "localhost", port: 1111, // 端口号 https: false, // https:{type:Boolean} open: true, //配置自动启动浏览器 // proxy: 'http://localhost:4000' // 配置跨域处理,只有一个代理 // 配置多个代理 proxy: { "/api": { target: "<url>", ws: true, changeOrigin: true }, "/foo": { target: "<other_url>" } } } };
如下内容依赖环境为 : vue-cli 版本2.9.x
项目github地址 :
安装完以上依赖后,就能够开始一个项目了,咱们先看下后端api的定义
请求
http request header{ //除登陆注册之外的请求,发起请求时要在请求头中加入token authorization:jwt } http request body{ }
返回
http response header{ } http response body{ code:业务处理状态码 msg:业务处理描述 token:jwt token data:业务数据 }
注:服务器端的host为118.24.85.97,端口为22222
1.测试api是否可用
2.注册
序号 | 参数名 | 是否必填 | 描述 |
---|---|---|---|
1 | name | y | 用户名 |
2 | pass | y | 密码 |
3.登陆
序号 | 参数名 | 是否必填 | 描述 |
---|---|---|---|
1 | name | y | 用户名 |
2 | pass | y | 密码 |
序号 | 参数名 | 描述 |
---|---|---|
1 | msg | ok |
2 | token | 用于验证用户身份的token |
4.获取当前用户信息
序号 | 参数名 | 描述 |
---|---|---|
1 | id | 用户id |
2 | token | 用于验证用户身份的token |
在终端中输入
vue init webpack vue2_template
而后会有一些选项让你选,按照项目需求选择,例如我不须要eslint,unit test,就能够选No,如今选no未来若是须要的话也能够本身安装
安装完成以后,按照提示切换到相应目录,执行相应指令,而后在浏览器打开网址,这样一个简单的vue项目就启动起来了
注意:
首先在src目录下新建一个文件夹views,用来放咱们的主要页面,而后在assets文件夹中创建fonts styles imgs,用来存放相应的资源,建完以后,文件夹以下
在这个项目中,咱们使用axios进行数据请求
axios中文文档: https://www.kancloud.cn/yunye...
# 安装axios npm/cnpm i axios -S # -S 指安装到package.json中的dependencies中
安装完成后,咱们要在main.js中引入,而后测试一下是否成功引入
//main.js文件 import axios from 'axios' axios.get('https://api.github.com/users?since=10') //使用github接口作一下测试 .then(res=>console.log(res)) .catch(err=>console.log(err))
浏览器显示如下信息,说明引入成功
github提供的接口配置了cors,因此咱们可以可以在浏览器正常访问到,但cors兼容性最低到ie10,并且后台不必定会配置cors,因此在开发时咱们须要配置一下跨域
参考连接:
参考文档: https://segmentfault.com/a/11...
先找个没有设置cors的api使用axios访问一下
axios.get('http://118.24.85.97:22222/api') .then(res=>console.log(res)) .catch(err=>console.log(err))
浏览器会由于同源策略报错
下面进行跨域的配置
配置目录 config/index.js 13行
proxyTable: { '/apis':{ target:'http://118.24.85.97:22222',//后台地址 proxyTable 把/apis映射成target 即 /apis=http://118.24.85.97:22222 changeOrigin:true,//是否跨域 pathRewrite:{ '^/apis':'' } } }
再进行访问数据时就要在接口前面加上/apis(/apis就至关于http://118.24.85.97:22222)
axios.get('/apis/api') .then(res=>console.log(res)) .catch(err=>console.log(err))
而后就发现浏览器访问成功了
proxyTable原理:跨域是浏览器禁止的,服务端并不由止跨域 ,因此浏览器能够发给本身的服务端而后,由本身的服务端再转发给要跨域的服务端,作一层代理。proxyTable使用的是http-proxy-middleware
中间件,内部用的是http-proxy
以上配置的跨域是开发环境下的,在生产环境就自动失效了,并且这样配置咱们开发时访问接口时,都要写成/apis/xxx/xxx
格式,在部署到服务器中时,咱们要把/apis拿掉,才能访问到正确的url。有两种方法,一种是在开发环境中设置(经过axios的baseURL),另外一种是在服务器上修改nginx的配置设置。
在这里详细说下第一种方式,原理是这样的:
经过检测是开发环境和生产环境,设置不一样的baseURL,使生产环境和开发环境都能正确访问url
在src目录下新建一个apis
目录,而后在apis目录下新建一个api.config.js
文件
//判断是不是生产环境 //webpack在开发环境和生产环境分别执行不一样的js文件,process.env.NODE_ENV设置了不一样的值,process.env.NODE_ENV在生产环境中值为'production'(这个值是在build/build.js中第4行设置的) var isPro = process.env.NODE_ENV=== 'production' // 若是是生产环境 咱们就使用服务器的uri,若是是开发环境,咱们就添加/apis前缀 module.exports = { baseUrl: isPro ? 'http://118.24.85.97:22222' : '/apis' }
在main.js中引入这个文件,而后设置axios的baseURL
//引入api.config.js文件,而后设置axios的baseURL import apiConfig from './apis/api.config' axios.defaults.baseURL=apiConfig.baseUrl
再来测试一下不加/apis的接口
axios.get('/api') .then(res=>console.log(res)) .catch(err=>console.log(err))
浏览器显示是ok的。这样咱们之后使用axios访问接口就能够不加/apis了,打包后访问也不用手动去除/apis
在vue项目开发过程当中,会涉及到不少接口的处理,当项目足够大时,就须要统一管理接口。具体方法应该挺多的,这里只介绍一种:使用axios+async/await进行接口的统一管理
通常来讲,后台的接口是分模块的,例如咱们后台的测试接口
咱们首先在src目录下新建一个apis文件夹,后台提供的全部接口都在这里定义
第二步,按照后台提供的模块新建js文件,咱们新建user.js
auth.js
第三步,引入axios,作相应的配置
在apis目录下新建一个http.js,在里面作axios相应的配置
import axios from 'axios' import apiConfig from './api.config' //建立axios的一个实例 var instance = axios.create({ baseURL:apiConfig.baseUrl, timeout: 6000 }) //------------------- 1、请求拦截器 后面介绍 instance.interceptors.request.use(function (config) { return config; }, function (error) { // 对请求错误作些什么 return Promise.reject(error); }); //----------------- 2、响应拦截器 后面介绍 instance.interceptors.response.use(function (response) { return response.data; }, function (error) { // 对响应错误作点什么 return Promise.reject(error); }); /** * 使用es6的export default导出了一个函数,导出的函数代替axios去帮咱们请求数据, * 函数的参数及返回值以下: * @param {String} method 请求的方法:get、post、delete、put * @param {String} url 请求的url: * @param {Object} data 请求的参数 * @returns {Promise} 返回一个promise对象,其实就至关于axios请求数据的返回值 */ export default function (method, url, data = null) { method = method.toLowerCase(); if (method == 'post') { return instance.post(url, data) } else if (method == 'get') { return instance.get(url, { params: data }) } else if (method == 'delete') { return instance.delete(url, { params: data }) }else if(method == 'put'){ return instance.put(url,data) }else{ console.error('未知的method'+method) return false } }
第四步,在apis/xxx.js
文件中引入http.js导出的函数,拿其中一个文件auth.js
说明
//auth.js 用于定义用户的登陆、注册、注销等 import req from './http.js' //定义接口 //在这里定义了一个登录的接口,把登录的接口暴露出去给组件使用 export const LOGIN =params=>req('post','/api/users/login',params) //这里使用了箭头函数,转换一下写法: // export const LOGIN=function(params){ // return req('post','/api/login',params) // } //定义注册接口 export const REG =params=>req('post','/api/users/reg',params)
最后一步,在须要用的该api的组件中引入并调用,咱们在App.vue文件中测试下
<template> <div> <h2>登陆</h2> 用户名<input type="text" v-model="user"> 密码<input type="password" v-model="pass"> <input type="button" @click="reg" value="注册"> <input type="button" @click="login" value="登陆"> </div> </template> <script> import {LOGIN,REG} from '../../apis/auth.js' export default { data() { return { user:'', pass:'', err:[] } }, methods: { async reg(){ try { const data = await REG({ name: this.user,pass: this.pass }) console.log(data) alert(JSON.stringify(data)) this.cleanForm() } catch (error) { console.log(error) } }, async login(){ try { const data = await LOGIN({ name: this.user,pass: this.pass }) alert(JSON.stringify(data)) this.cleanForm() } catch (error) { console.log(error) } }, cleanForm(){ this.user='' this.pass='' } }, } </script>
注:若是要打开Login.vue,须要配置对应的路由
上面的代码引入了auth.js
定义的api,并在对应的方法中使用。代码中用到了async/await,其实很简单,能够假设async是个标识,说明这个函数中有异步请求,await翻译为'等',后面接一个异步请求,等后面的异步请求执行完成以后,会把结果赋给=
左边的值
参考连接 http://www.runoob.com/w3cnote...
总结一下,像上面那样定义接口虽然麻烦点,但有两个好处:
Vue Router官方文档 https://router.vuejs.org/zh/
路由的配置文件在router/index.js文件中先引入文件,再进行配置
首先在views目录中新建如下页面
,主页(Home/Home.vue),登陆页(Login/Login.vue),测试页(Test/Test.vue)
而后配置下路由
import Vue from 'vue' import Router from 'vue-router' //@表示 src目录 webpack的配置在webpack.base.conf.js第29行 alias{'@':resolve('src')} import Home from '@/views/Home/Home.vue' import Login from '@/views/Login/Login.vue' import Test from '@/views/Test/Test.vue' Vue.use(Router) export default new Router({ routes: [//路由规则 { path: '/', name: 'Home', component: Home }, { path:'/login', name:'Login', component:Login }, { path:'/test', name:'Test', component:Test } ] })
路由规则在routes
中进行配置,routes
是一个数组,接受一系列路由规则,每一个路由规则是一个对象,包括路径、路由名字,和路径匹配的组件,建议给每一个路由加个名字,在后面可能会用到。
打开浏览器,输入相应的url查看配置的路由是否正确,不正确的话检查下本身的配置
参考文档:路由懒加载官方文档:https://router.vuejs.org/zh/g...
webpack之mainfest解读:https://github.com/younth/blo...
当打包构建应用时,Javascript 包会变得很是大,影响页面加载。若是咱们能把不一样路由对应的组件分割成不一样的代码块,而后当路由被访问的时候才加载对应组件,这样就更加高效了。因此,懒加载的含义是当路由被访问时再去加载对应的js代码。
首先,不作路由懒加载的状况下,咱们打包一下(切换到项目目录,执行npm run build
),而后会发现项目下生产了3个js文件
简单介绍一下做用:
而后咱们实现一下路由懒加载 @/router/router.js
import Vue from 'vue' import Router from 'vue-router' // import Home from '@/views/Home/Home.vue' // import Login from '@/views/Login/Login.vue' // import Test from '@/views/Test/Test.vue' // 懒加载方式 const Home=()=>import('@/views/Home/Home.vue') const Login=()=>import('@/views/Login/Login.vue') const Test=()=>import('@/views/Test/Test.vue') Vue.use(Router) export default new Router({ routes: [ { path: '/', name: 'Home', component: Home }, { path:'/login', name:'Login', component:Login }, { path:'/test', name:'Test', component:Test } ] })
懒加载只是改变了一下组件的引用方式,由原来的直接引入变成异步引入,当咱们访问对应的路由path时,才会加载相应的路由组件。
配置完成后再执行一次打包,结果以下:
咱们会发现目录中多出来3个js文件,而且app.js
文件变小了。这说明配置了懒加载以后,app.js中其余组件的内容被抽离出来,分配到各自的js文件中。配置懒加载以后,刚开始打开页面只会加载app.js文件,只有在用户点击相应路由时,才会加载对应的js代码。当咱们的业务代码很是多时,懒加载是个很好的选择。
官方文档: https://router.vuejs.org/zh/g...
配置history模式有两个缘由,一是由于hash模式看很丑,二是由于预加载要用到History模式,配置很是简单,只须要配置属性mode
的值为'history'
const router = new VueRouter({ mode: 'history', routes: [...] })
不过这种方式须要后台的支持,当匹配不到url时,返回url/index.html页面
nginx配置以下
location / { try_files $uri /index.html; }
参考连接:json web token入门教程 http://www.ruanyifeng.com/blo...
jwt官网 https://jwt.io/
咱们经过jwt进行用户认证,jwt的原理是:服务器认证之后,生成一个json对象,发回给用户.
{ "id":"001", "姓名":"小明", "角色":"管理员", "到期时间":"2019年3月3日12时30分" }
之后用户与服务端通讯的时候,都要发回这个json对象。服务器彻底靠这个对象认定用户身份(通常是经过这个对象的中id去数据库请求数据)。为了防止用户篡改数据,服务器会在生成这个对象的时候,加上签名。就像这种形式:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
关于JWT保存更新的业务流程以下:
主要逻辑包括:
localStorage
中保存token//1.登陆以后保存token login.vue async login(){ const data = await LOGIN({ name: this.user,pass: this.pass }) //保存token localStorage.setItem('token',data.token) //查看是否保存成功 console.log(localStorage.getItem('token')) }
//每次发送请求以前,讲token放到请求头中 api/http.js //---使用axios的请求拦截器,每次发送请求以前拦截一下 instance.interceptors.request.use(function (config) { // 给头添加token if (localStorage.getItem('token')){//存在token,加入头 config.headers.authorization=localStorage.getItem('token') } return config; }, function (error) { // 对请求错误作些什么 return Promise.reject(error); }); //完成以后,记得发送一个请求,看看是否正确添加token //---响应拦截器,服务器响应后先到达这里 instance.interceptors.response.use(function (response) { if(response.data.code=='2000'){//成功响应,更新token if(response.data.token){ localStorage.setItem('token',response.data.token) } }else{ //错误处理 根据不一样的状态码,进行错误处理 } return response.data; }, function (error) { // 对响应错误作点什么 return Promise.reject(error); });
除了对token的操做,咱们还要判断用户有没有权限访问这个页面(有些页面是用户必须登陆才能访问的),具体配置要使用Vue Router的导航守卫
参考连接: https://router.vuejs.org/zh/g...
在全局前置守卫中进行验证
//在router/index.js进行配置 //在每次进行路由跳转以前进行 router.beforeEach((to,from,next)=>{//增长登陆验证 const isLogin=localStorage.getItem('token')?true:false; if(to.path=='/login'){//若是是登陆页面,不须要token next(); }else{//若是不是登陆页面就要判断是否登陆 isLogin?next():next('/login'); } })
iview官网: https://www.iviewui.com/
为节省开发时间,咱们每每会使用一些第三方ui库,好比iview elementui等
咱们在这里只介绍iview,其余ui库大同小异
cnpm i iview --save
官网说,须要下载插件才能按需引入,官网说明,可是不下好像也能够正常引入
//在main.js文件中引入项目须要的组件 import {Button,Table,Message} from 'iview' //而后注册组件 Vue.component('Button',Button) Vue.component('Table',Table) Vue.component('Message',Message)
这样注册的话太繁琐,因此须要优化一下
//main.js import {Button,Table,Message} from 'iview' const iviewComs={Button,Table,Message} Object.keys(iviewComs).forEach(key=>{Vue.component(key,component[key])})
代码都写在main.js中显得太拥挤,咱们能够把代码拿出去,写成一个插件
咱们在components文件夹中新建一个文件iview-coms
,用来放iview中引入的组件
//components/iview-coms.js import {Button,Table,Message} from 'iview' const components={Button,Table,Message} const install = function(Vue, opts = {}){ Object.keys(components).forEach(key=>{ Vue.component(key,components[key]) }) } export default install
而后在main.js中引入,use
这个插件
import iviewComs from './components/iview-coms' Vue.use(iviewComs)
ok了,接下来看自定义主题
官网连接:https://www.iviewui.com/docs/...
原理很简单,就是把ivew的less文件引入,而且覆盖掉,而后在main.js文件中引入本身的less文件
首先,咱们须要下载解析less文件的loader ,less
和less-loader
,这里有个坑,下载less的时候要下载3版本如下的,否则会报一堆错误
cnpm i less@2.7.2 less-loader -D
下载完就ok了,不须要在webpack中进行配置,由于已经配置好了
而后,在assets/styles/base.less(没有须要本身新建)中,引入iview的样式文件,而且覆盖掉
默认变量列表:https://github.com/iview/ivie...
//assets/styles/base.less //------ 引入iview样式 @import '~iview/src/styles/index.less'; //------ 覆盖iview的样式 @primary-color: #E91E63; @error-color : #FF3300;
最后在main.js引入该less文件
//main.js import './assets/styles/base.less'
此时,引入的组件就能够在.vue文件中使用了,看一下效果:
ok了。最后还要补充一下,在项目开发过程当中,不可避免的要覆盖iview默认的样式,咱们分为两种状况,一种是全局覆盖,一种是局部覆盖。
全局覆盖的话咱们要新建一个less文件,好比叫cover-iview.less
全部覆盖iview样式的代码都放在这里,而后在base.less中引入这个文件。
局部覆盖的话要注意不要影响到别的样式,因此要充分利用less的做用域,例如咱们只须要改home页面下的iview按钮样式,咱们能够这样:
.home{ .ivu-btn{ } }
参考文档:vue插件说明:https://cn.vuejs.org/v2/guide...
项目中每每会使用一些通用的函数,好比获取当前时间、时间格式转化,防抖,节流等,咱们能够把这个公用的部分封装成插件,在main.js中引入。
首先,在src目录下新建utils
文件夹,在里面新建index.js
,utils.js
文件
咱们在utils.js
中编写本身的工具库,而后导出
class Utils{ constructor(){ this.d=new Date();//date对象 this.instance=null; } static getInstance(){//单例模式 if(!this.instance){ this.instance = new Utils(); } return this.instance; } pick(obj,arr){//pick({ a: 1, b: '2', 'c': 3 }, ['a', 'c']) =>{a:1,c:3} return arr.reduce((acc,curr)=>{ return (curr in obj && (acc[curr] = obj[curr]), acc) },{}) } dateFormat(datetime,pattern=""){ let vWeek = ["星期天","星期一","星期二","星期三","星期四","星期五","星期六"]; let dt=new Date(datetime); let y=dt.getFullYear(); let m=(dt.getMonth()+1).toString().padStart(2,'0'); let d=dt.getDate().toString().padStart(2,'0'); let hh=dt.getHours().toString().padStart(2,'0'); let mm=dt.getMinutes().toString().padStart(2,'0'); let ss=dt.getSeconds().toString().padStart(2,'0'); let vWeek_s = dt.getDay();//星期 if(pattern.toLowerCase() === 'yyyy-mm-dd'){ return `${y}-${m}-${d}` }else if(pattern.toLowerCase() === 'mm-dd'){ return `${m}-${d}` }else if(pattern.toLowerCase() === 'yyyymmddhhmmss'){ return `${y}${m}${d}${hh}${mm}${ss}` }else { return `${y}-${m}-${d} ${hh}:${mm}:${ss} ${vWeek[vWeek_s]}` } } } const UTIL = Utils.getInstance(); // console.log(UTIL.dateFormat(new Date(),'yyyymmddhhmmss')) //=>20190312110722 // console.log(UTIL.dateFormat(new Date()))//=>2019-03-12 11:07:22 星期二 // console.log(UTIL.pick({ a: 1, b: '2', 'c': 3 }, ['a', 'c']))//=>{a:1,c:3} export default UTIL;
而后在index.js中编写插件,导出
//utils/index.js import UTIL from './utils.js' const UtilPlugin={} UtilPlugin.install=function(Vue,options){//插件必须有install方法,接受两个参数,一个是Vue构造器,一个是参数 Vue.prototype.$utils=UTIL//在vue prototype上添加实例方法 } export default UtilPlugin
最后在main.js中引入并use插件
// utils import Util from './utils/index' Vue.use(Util) console.log(Vue.prototype.$util)//打印下是否引入成功
以后就能够在组件中经过使用this.$utils
调用方法了
咱们的目标是兼容到ie9,对ie8及如下的浏览器作相应的跳转处理(跳转到浏览器下载界面)兼容性对一个程序来讲是很是重要的,兼容性测试越早越好
在项目根目录下中的html中head中加入下面代码
<!--[if lte IE 8]><script>window.location.href="https://support.dmeng.net/upgrade-your-browser.html?referrer="+encodeURIComponent(window.location.href);</script><![endif]-->
目的是检测ie浏览器的版本,若是低于<=ie8,就跳转到下面这个页面
参考连接: https://juejin.im/post/5b2868...
咱们把浏览器调到ie9,而后看控制台报错信息
报这个错的缘由是es6的新对象,新表达式,ie9不支持,为解决这个问题,咱们须要引入babel-polyfill
cnpm i babel-polyfill -D
安装完成以后,在main.js文件中引入
import 'babel-polyfill'
在项目使用 vue-cli
生成的代码中,根目录有一个 .babelrc
文件,这是项目使用 babel 的配置文件。在默认生成的模板内容中,增长 "useBuiltIns": "entry"
的设置内容,这是一个指定哪些内容须要被 polyfill(兼容) 的设置
useBuiltIns 有三个设置选项
false
- 不作任何操做entry
- 根据浏览器版本的支持,将 polyfill 需求拆分引入,仅引入有浏览器不支持的polyfillusage
- 检测代码中 ES6/7/8
等的使用状况,仅仅加载代码中用到的 polyfill加入这些代码后,工程中大部分代码已能够兼容到ie9版本,但仍是会有少部分不兼容的特性,例如requestAnimationFrame
、classList
等。对于这些内容,咱们须要本身定义polyfill来解决,在src目录下新建一个文件夹polyfill,而后在polyfill文件夹下面建一个polyfill.js,咱们在polyfill.js中加入咱们的兼容代码
而后在main.js中引入这个文件
import './polyfill/polyfill'
解决兼容方式的正确姿式是:拿到ie9浏览器下的报错信息,去goole或者baidu搜索,获得polyfill,而后加入到本身的polyfill.js文件中
咱们执行一下npm run build
,结果以下:
整个打包过程花了32s左右,如今咱们的项目只是引入了相关的依赖,一些业务逻辑尚未写,打包速度就那么慢了,等到咱们写完整个项目,打包速度还会继续变长,因此咱们须要优化一下。
优化打包速度,咱们修改的主要是
webpack.prod.conf.js
文件
Webpack 默认提供的 UglifyJS 插件,因为采用单线程压缩,速度慢 ;
webpack-parallel-uglify-plugin 插件能够并行运行 UglifyJS 插件,更加充分而合理的使用 CPU 资源,这能够大大减小的构建时间;
//安装 cnpm i webpack-parallel-uglify-plugin -D
//配置 webpack.prod.conf.js //首先删除项目中的 UglifyJsPlugin插件及配置,第二次打包时提升速度,要把.cache文件加入到gitignore中 // new webpack.optimize.UglifyJsPlugin({ // compress: { // warnings: false, // drop_console: true // }, // sourceMap: true // }), //而后引入并使用咱们刚才装的插件
==注意:版本控制工具提交时,要忽略.cache
文件==
配置完后咱们执行npm run build
,发现打包速度降到了23s
再执行一次npm run build
,发现打包速度降到了12s
时间下降那么可能是由于文件没有改动,直接利用了缓存中的js文件
通常node.js是单线程执行编译,而happypack则是启动node的多线程进行构建,大大提升了构建速度。
首先安装,
修改webpack.base.conf.js
const HappyPack = require('happypack'); const os = require('os'); const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length }); ... ... // 增长plugins plugins: [ new HappyPack({ id: 'happy-babel-js', loaders: ['babel-loader?cacheDirectory=true'], threadPool: happyThreadPool, }) ] ... ... // 修改对应loader { test: /\.js$/, loader: 'happypack/loader?id=happy-babel-js', include: [resolve('src'), resolve('test')], }
配置完成,执行npm run build
what??并无提升速度 不要用这个鬼东西了
https://github.com/mzgoddard/...
#安装 cnpm install --save-dev hard-source-webpack-plugin
使用,在webpack.prod.conf.js中引入并使用
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin'); module.exports = { context: // ... entry: // ... output: // ... plugins: [ new HardSourceWebpackPlugin() ] }
结果:
注:要第二次打包才生效
总结下,使用了三个插件,咱们的打包速度从30s下降到4s,awesome!
首先要说明一下,首屏加载速度优化针对的是打包后dist文件。咱们若是要在本地进行测试的话,须要本地有个服务器,咱们在这里使用nginx。
下载地址: http://nginx.org/en/download....
在官网上找到本身系统适合的nginx版本,下载到本地
#1.解压文件 tar -xzf nginx-1.14.0.tar.gz #mac可使用解压缩工具解压,没必要用命令行 #2. 配置安装路径 --prefix指定安装路径 假设我要装到/usr/local/nginx文件夹中 ./configure --prefix=/Users/best9/local/nginx #编译 make ##安装 make install
安装完成后进入到—prefix
指定的文件夹中,执行ll
,会发现文件夹下有如下目录
咱们要关心就是我上面标出来的三个目录
进到sbin目录中,启动nginx程序
cd sbin #须要使用root权限,不然会报错 报错信息能够在日志中查看到,错误日志目录 /logs/error.log sudo ./nginx
正常的话,nginx会默认在localhost:80端口启动,在浏览器访问localhost
,就会显示默认界面
若是电脑的80端口被占用的话,在conf/nginx.conf
文件中修改端口
nginx使用-s发送信号操做运行中的进程,经常使用命令以下:
注意:使用命令须要在sbin
目录下
#启动nginx ./nginx #当即中止服务 -s stop ./nginx -s stop #优雅地中止服务 -s quit ./nginx -s quit #重启服务 -s reload ./nginx -s reload
咱们在这里使用nginx配置一个最简单的静态文件服务器,更复杂的配置稍后再讲
nginx的配置文件地址:conf/nginx.conf
使用vim或者其余编辑器打开该文件,修改配置文件第43-45行:
vim conf/nginx.conf
location / { alias /Users/best9/github/vue2_template/dist; #访问/至关于访问alias配置的目录 }
配置完成后保存,而后重启服务
sudo ./sbin/nginx -s reload
要使用root权限重启
打开浏览器访问localhost
由于没有登陆,会自动跳转到登陆界面
到这里静态文件服务器就配置好了,但咱们刷新下页面,会报错404
这是由于咱们使用了vue router的history模式,咱们须要在nginx中加入如下配置
location / { try_files $uri $uri/ /index.html; }
而后重启nginx,再刷新页面就没问题了
以上步骤就绪后,咱们就能够来优化加载速度了
打开chrome的devTools面板,切换到Network
,禁用浏览器缓存,刷新测试下加载速度,发现整个应用加载大约须要1.97s,以下图:
把网络环境切换到Fast 3G
,再测试一次,发现加载用了7.56s,白屏时间6.89s
咱们使用预渲染插件进行优化
使用插件:prerender-spa-plugin
首先,安装 prerender-spa-plugin
,安装时件略长,由于其依赖了 phantomjs
cnpm install prerender-spa-plugin --save-dev
咱们只在生产环境中进行预渲染,修改 build/webpack.prod.conf.js
,在配置插件的地方加入以下代码。
//引入 预渲染插件 const PrerenderSpaP=require('prerender-spa-plugin') //在plugins中配置 new PrerenderSpaP( // 输出目录的绝对路径 path.join(__dirname,'../dist'), //预渲染路由 ['/home','/login'] )
再次执行打包,而后再进行测试:
发现白屏时间为4.10s,在弱网环境下,使用预渲染,大约能缩减2.5秒的白屏时间
举个例子:
插件配置以下:
new PrerenderPlugin({ staticDir:path.join(__dirname,'../dist') routes:['/','/about','/login'] })
路由配置以下:
gzip官方文档 http://nginx.org/en/docs/http...
nginx默认是关闭gzip的,咱们须要本身打开,并进行一些配置:
gzip:on; #打开gzip,关闭为off gzip_min_length 1; #小于gzip_min_length,不进行压缩(默认单位为byte) gzip_comp_level 2; #压缩级别 gzip_types text/plain text/css application/javascript text/javascript image/jpeg image/gif image/png;#指定类型进行gzip压缩
配置完成后,咱们再测试一下加载速度:
发现白屏时间为1.95s,加载文件的体积也变小了
咱们要在本地部署测试,因此后台的地址是127.0.0.1:22222
项目开发完成后须要部署到服务器,由于是先后端分离,因此前端的应用部署到nginx,后端的应用部署到本身对应的服务器,因此咱们须要配置一下,把后端的服务器变成上游服务,nginx作反向代理服务器
反向代理:服务器根据客户端的请求,从其关系的一组或多组后端服务器上获取资源,而后将这些资源返回给客户端。
因为上游服务器(后台服务器)要处理很是复杂的逻辑,因此性能不怎么样,咱们使用nginx做为反向代理服务器后,能够将请求按照负载均衡算法代理给多台上游服务器。配置以下:
以上配置是将全部的请求转发给上游服务器,但若是咱们只想将动态请求转发给上游服务器,静态资源由nginx本身处理,就能够这样作:
判断是不是后台api(根据location的匹配规则),若是是的话,就进行转发
匹配规则看这里:https://stackoverflow.com/que...
upstream local{ server 127.0.0.1:22222; #假设在本地部署 } server{ listen:80; server_name localhost; location ~ /api/ { #以`/api/`开头的uri就行转发,不然不转发 ~表明正则表达式匹配 proxy_set_header: Host $host; proxy_set_header: X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_pass http://local; } location / { #... alias index等配置 } }
这里须要注意一个问题:proxy_pass是转发请求的模块,当你访问localhost:80/api/users/login
时,会被转发到local
的地址,即127.0.0.1:22222/api/users/login
,因此开发环境下访问后台接口的URI要写你部署到nginx的URI,而不是真正的后台地址(由于被转发了)
前端配置
//apis/api.config.js //判断是不是生产环境 var isPro = process.env.NODE_ENV=== 'production' module.exports = { baseUrl: isPro ? 'http://localhost:80' : '/apis'//生产环境下的baseURl是nginx的hoost:port }
项目作完须要发布到服务器,但每次手动打包,而后ftp传上去的话就太麻烦了,因此咱们的需求是:git或者svn提交后,自动打包发布到服务器。使用的工具是jenkins.
参考文档: https://juejin.im/post/5ad198...
jenkins通常状况下会装在服务器,但若是是同一个局域网的话,装在本机也能够
linux:
windows下:
java -jar jenkins.war
便可。mac:
在主页上点击建立
直接点保存
,而后去安装插件
首先返回主页,而后点击左侧菜单 系统管理
->插件管理
须要安装的插件有:
安装插件的方式:
安装完插件以后重启一下jenkins(安装完插件后,有个重启的选项,勾选便可)
当咱们向github/码云等远程仓库push咱们的代码时,jenkins能知道咱们提交了代码,这是自动构建自动部署的前提,钩子的实现原理是在远端仓库上配置一个Jenkins服务器的接口地址,当本地向远端仓库发起push时,远端仓库会向配置的Jenkins服务器的接口地址发起一个带参数的请求,jenkins收到后开始工做
打开建立的项目(进入工程->点击配置
)
构建触发器
勾选 Generic Webhook Trigger
github仓库配置钩子:
进入github项目中该项目页面,点击setting
->webhooks
,添加payload URL,
URL格式为 http://<User ID>:<API Token>@<Jenkins IP地址>:端口/generic-webhook-trigger/invoke
userid和api token在jenkins的系统管理
-管理用户
-选择你的用户点进去
-左侧设置
里
自动化构建:jenkins实现安装依赖,打包(npm install && npm run build),此外还能够执行一些测试行为
点击构建环境
,勾选nvm
,输入node版本
点击构建
,选择执行shell
,输入执行命令,多个命令使用&&分开
npm config set registry http://registry.npm.taobao.org/ && npm install && npm run build