别慌,内容看起来多,其实都是一些小干货啦
搭建一个vue项目:
配置相应的环境,如安装node,vue,webpack(最好安装一下淘宝镜像 cnpm)
使用node自带镜像或者淘宝镜像安装vue脚手架和webpack
cnpm install webpack -g
cnpm install -g vue-cli
安装完成之后用 webpack -v和vue -V查看安装的版本
在本地合适位置创建一个文件夹并命名(例如 vue-demo)
打开cmd使用cd进入到这个文件夹的位置
使用命令
vue init webpack vue_demo //创建一个名叫vue_demo的vue项目
(解释一下这个命令,这个命令的意思是初始化一个项目,其中webpack是构建工具,也就是整个项目是基于webpack的。其中vue_demo是整个项目文件夹的名称,这个文件夹会自动生成在你指定的目录中。注意这里的项目名称不要使用大写字母)
上述选择说明:
Project name——项目名称(直接回车就是默认你之前写的第一条命令后面那个名字),也可以自定义一个
Project description——项目的描述
Author——项目作者开发者
Vue build——默认选择
Install vue-router?——是否创建路由,这个一般选择yes,也可以自己创建路由
Use ESLint to lint your code?——是否使用eslint规范你的代码,一般选择yes
Should we run `npm install` for you after the project has been created? (recommended)是否使用镜像安装依赖包
根据提示,进入到项目文件夹里,使用npm run dev 启动项目
然后出现这个就代表你的vue项目创建成功了,通过给出的地址,就可以进如到初始化的项目中
这是创建完的项目的文件目录结构,主要文件在src里面,node_modules是刚刚项目创建过程中,使用npm install命令创建的依赖包,整个项目所需的资源文件都在这个里面,其中src里面的components文件夹里面存放的是vue的单文件组件,用来存放组件的文件夹,router文件夹里面的index.vue是用来写vue路由的文件,实现页面间的跳转,APP.vue是项目的主文件,也是入口文件,main.js是vue实例的挂载地,通过这个文件,我们可以导入我们所需的资源文件,例如一些初始化css文件,assets是资源文件,可以存放一些项目所需的图片图标等
封装全局axios请求:
// axios 配置
import axios from 'axios'
import store from './store'
import router from './router'
//创建 axios 实例
let instance = axios.create({
baseURL: "", //本地打开项目访问地址,切记需要上线是要改为服务器地址
timeout: 5000, // 请求超过5秒即超时返回错误
headers: { 'Content-Type': 'application/json;charset=UTF-8' },
})
instance.interceptors.request.use(
config => {
if (store.getters.token) { // 若存在token,则每个Http Header都加上token
config.headers.Authorization = `token ${store.getters.token}`
console.log('拿到token')
}
console.log('request请求配置', config)
return config
},
err => {
return Promise.reject(err)//基于promise的axios
})
// http response 拦截器
instance.interceptors.response.use(
response => {
console.log('成功响应:', response)
return response
},
error => {
if (error.response) {
switch (error.response.status) {
case 401:
// 返回 401 (未授权) 清除 token 并跳转到登录页面
store.commit('BIND_LOGOUT')
router.replace({
path: '/login',
query: {
redirect: router.currentRoute.fullPath
}
})
break
default:
console.log('服务器出错,请稍后重试!')
alert('服务器出错,请稍后重试!')
}
}
return Promise.reject(error.response) // 返回接口返回的错误信息
}
)
export default {//将请求暴露到全局
// 注册
userRegister(data) {
return instance.post('/api/register', data)//这个地址是后端给你的,前缀一般都是统一api
},
// 登录
userLogin(data) {
return instance.post('/api/login', data)
},
//get请求
getsome() {
return instance.get('/api/some')//get请求使用方法
},
}
但是这只是封装,并不能使用,需要使用跨域进行代理,否则,不同端可能会无法访问
跨域代理
//跨域写在vue.config.js里面
module.exports = {
runtimeCompiler: true,
publicPath: './', // 设置打包文件相对路径, baseUrl 已被官方不建议使用
devServer: {
// host: 'localhost',
port: 8081,
// disableHostCheck: true,
open: process.platform === 'darwin', //配置自动启动浏览器
https: false,
hotOnly: false,
proxy: { // 跨域请求代理
'': {//这里的''里面的内容意思就是用''里面的内容代替你的后端接口地址,一般写api
target: '', // 后端接口地址
changeOrigin: true,
ws: false,
pathRewrite: {
'^': ''//后端接口地址
}
}
}
},
productionSourceMap: true, // 生产环境是否生成 sourceMap 文件
// configureWebpack: {
// performance:{ // webpack打包压缩限制报错这里禁掉这个warning信息
// hints: false
// }
// }
}
设置好之后就可以使用axios(前提后端得有接口哈)发送请求了
路由部分
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'HelloWorld',
component: HelloWorld
}
]
})
//创建全局导航钩子(这个.是跟在new Router()后面的)
.beforeEach((to, from, next) => {
if (to.meta.title) { // 路由发生变化修改页面title
document.title = to.meta.title
}
if (to.meta.requireLogin) {
if (store.getters.token) {
if (Object.keys(from.query).length === 0) { // 判断路由来源是否有query,处理不是目的跳转的情况
next()
} else {
let redirect = from.query.redirect // 如果来源路由有query
if (to.path === redirect) { // 避免 next 无限循环
next()
} else {
next({ path: redirect }) // 跳转到目的路由
}
}
} else {
next({
path: '/login',
query: { redirect: to.fullPath } // 将跳转的路由path作为参数,登录成功后跳转到该路由
})
}
} else {
next()
}
})
打印实现
//首先,引入第三方的打印插件
npm install vue-print-nb --save
//引入之后,在需要打印的模块使用v-print指令,即可实现打印
例如
这里v-print后面的 #printTest 是绑定的需要打印的模块
这里是打印的地方(这里放打印的内容,表格等)
登陆校验
//登陆和注册时都使用vue里面rule校验规则,例如
data(){
let checkName = (rule, value, callback) => {//校验函数
if (value === '') {
callback(new Error('请输入用户名'))
} else {
callback()
}
}
let validatePass = (rule, value, callback) => {//校验函数
if (value === "") {
callback(new Error("请输入密码"))
} else {
callback()
}
}
return {
ruleForm: {//定义ruleForm保存输入内容
name: '',
pass: ''
},
rules: {//定义rules规则,将校验函数添加至rules规则中进行校验
name: [{ validator: checkName, trigger: 'change' }],
pass: [{ validator: validatePass, trigger: 'change' }],
}
}
}
注册
//在注册部分发送的密码要加密,可以采用md5加密方式
//加密方式
import CryptoJS from 'crypto-js' // 引用 md5 加密
axios.uregister({
username: encodeURIComponent(this.ruleForm.username),
password: CryptoJS.MD5(this.ruleForm.pass).toString(),//这里使用CryptoJS.MD5().toString()方法加密发送给后端的密码
phone: this.ruleForm.phone,
code: this.ruleForm.smscode
}).then(res => {
console.log("^^^")
})
APP开发,采用的是Hbuliderx编辑器,使用了colorui、uvew等组件库,同时也使用了uniapp自带组件库
文件结构
当我们创建了一个uniapp项目后,大致的文件结构就是这样的(介绍有可能不是很准确,有需要可以参考官网哈)
- 首先,common文件夹下面就是存放封装好的请求的文件,其中request.js是主要的封装文件(下文有介绍),http.apis.js文件是存放请求函数的,index.js主要是存放一些请求配置的
- comments文件夹下面存放的主要是引用的组件
- js_sdk存放的是配置一些特殊接口的sdk
- node_modules是依赖包
- pages下是写APP中整体页面的,下面分散多个页面的文件(.vue)
- static文件夹是存放一些资源文件的
- store文件夹使用来存放缓存文件的,其中index文件下文有介绍
- unpackage文件夹里面可以存放APP整体的图片和在线打包后的apk文件
- APP.vue是项目的主入口文件
- main.js是主文件
- manifest.json文件是全局配置文件
- pages.json文件用于存放各个页面路径以及设置全局页面的统一样式
首先通过promise对uniapp的自带的request请求进行封装
1、首先创建一个request.js文件,再定义基地址
const base_url = "http://……/app/"
2、实现封装请求
// 请求接口封装
const requests = (_options, errorlog = true, mask = true, redirect = null) => {
return new Promise((resolve, reject) => {
// 是否显示加载模糊
if (mask) {
uni.showLoading({
title: '正在加载中...',
mask: true
});
}
let _data = {}
let _header = {}
if (_options.hasOwnProperty("data")) {
_data = _options.data
//console.log(_data)测试传递的参数是否准确
}
if (_options.hasOwnProperty("header")) {
_header = $.extend(header, _options.header)//将参数中的header传递给后端,这里使用了jQuery的方法,但是也可以不使用,直接将参数中的header给这里的_header
}
console.log( _options.url.indexOf("://") == -1 ? base_url + _options.url : _options.url)//测试请求地址是否正确
uni.request({
url: _options.url.indexOf("://") == -1 ? base_url + _options.url : _options.url,//请求地址
method: _options.method,//请求方法
data: _data,//请求的数据
header: _header,//请求头
success: (res) => {
if (res.statusCode >= 500) {
return
}
if (res.statusCode < 300) {
if (res.data.code != '200' && res.data.msg != 'OK') {//通过判断后端传回来的数据是否满足要求
uni.showToast({
icon: 'none',
title: res.data.msg
})
}
console.log(res.data)
resolve(res.data)//成功,则进入resolve()方法中,将传回来的参数传递给需要的then方法
} else {
// 未登录状态或者登陆状态失效。需要提示重新登陆。
if (res.statusCode == 401) {
uni.showModal({
title: '需要登录',
content: "此功能需要登录使用哦",
showCancel: false,
confirmText: '去登录',
success: function(res) {
// uni.redirectTo({
// url: '/pages/login/login.vue'
// });
}
})
} else if (errorlog) {
let str = ""
for (let key in res.data) {
str += res.data[key] + '\n'
}
uni.showModal({
title: "错误",
content: str,
showCancel: false,
confirmText: '知道了',
success: function() {
if (redirect) {
uni.reLaunch({//uniapp中的relaunch方法是关闭所有页面,跳转到目的页面
url: redirect
})
}
}
})
}
}
},
fail: (err) => {
if (mask) {
uni.showToast({
icon: 'none',
title: '服务器错误~请联系管理员',
duration: 4000
})
}
reject(err)//错误则进入到reject方法中
},
complete: () => {
if (mask) {
uni.hideLoading() // 完成uni.showLoading回滚
}
}
});
})
}
//接着导出这个请求
module.exports = {
requests: requests,
}
//再创建一个http.api.js文件
//这个文件专门存放请求函数
//例如
import api from '@/common/request.js'//先引入刚刚封装好的请求方法,api作为别名调用方法
// 登陆
function Login(options, errorlog = true, mask = true){//使用es6语法,使用默认参数
return api.requests({
url:'/login',//这个地址就是后端给你的接口文档里面的
method: 'post',//请求方法
data:options//从页面传递过来的参数,发送给后端
})
}
//接着把写好的请求暴露到全局
export default {
Login
}
在登陆界面是这样的
this.$httpApis.Login({//这里的$httpApis是在main.js里面挂载到vue实例上的(见下方main.js),所以可以在全局引用
//这个对象就是需要传递给后端的数据
loginname:_this.phoneData,
password:_this.passData
})
.then(res => {
console.log(res)
// 简单验证下登录(不安全)
if(res.code == 200){
console.log("登陆成功")
uni.reLaunch({
url: './home',
});
}else if(res.code == 401){
_this.isRotate = false
uni.showToast({
icon: 'none',
position: 'bottom',
title: res.msg
});
}
uni.hideLoading();
}).catch(err => {
uni.hideLoading();
})
main.js
import Vue from 'vue'
// 将请求类挂载到 Vue 实例上
Vue.prototype.$httpApis = httpApis
// 将请求路径挂载到 Vue 实例上
Vue.prototype.$api = api
缓存
缓存在前端十分重要,可以避免很多不必要的重复请求,过多的重复性请求会占用后端资源,影响性能,vue提供了一种缓存管理机制——vuex,但是我这个项目中使用的是uniapp开发一个APP,使用vuex会导致一个很严重的问题>在页面刷新之后会导致vuex里面的存储内容被刷新掉,导致缓存效果失败。uniapp里面自带了缓存的api,但是存储很多信息时就会导致信息错乱,没有条理,所以我就采用vuex和uniapp自带的uni.setstorage()方法结合的方式,实现了信息条理化存储,实现demo如下
//此处为store下的index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({//vuex知识具体请看vuex官网
state: {
hasLogin:false, //用户是否登录
userInfo:{} //用户数据
},
mutations: {
// 登录
Login(state,user){//登陆函数,登陆成功后调用,缓存需要缓存的信息
//登录状态为已登录
state.hasLogin = true
state.userInfo = user
//储存用户数据到本地
uni.setStorage({
key: 'userInfo',
data: user,
});
console.log('登陆成功')
console.log(state.hasLogin,state.userInfo)
},
// 退出登录
logout(state,user){
//登录状态为未登录
state.hasLogin = false
state.userInfo = {}
//删除本地储存
uni.removeSavedFile({
key: 'userInfo',
})
console.log('退出登录')
console.log(state.hasLogin,state.userInfo)
}
},
modules: {}
})
export default store;
login.vue
import { mapMutations } from 'vuex'//从vuex中引入mapMutations
methods: {
...mapMutations(['Login']),//将this.login映射为this.$store.commit('login')
this.$httpApis.Login({
loginname:_this.phoneData,
password:_this.passData
})
.then(res => {
console.log(res)
// 简单验证下登录(不安全)
if(res.code == 200){
_this.isRotate=false
let userInfo={
"username":res.data.userinfo.name,
"nickname":res.data.userinfo.userid,
"token":res.data.token,
} //保存用户信息和token
this.Login(userInfo);//调用vuex中的登陆函数,存入缓存
console.log("登陆成功")
uni.reLaunch({
url: '../home/home',
});
}
}
APP内某个页面跳转到一个网页
在uniapp中有专门存放网页的标签,但是在项目中,甲方给我们提供跳转的网站的后缀是.action结尾的网址,使用这个标签会失效,显示无法加载网页资源,所以不得不换一种方式,想到了h5的plus.runtime.openURL方法,但是使用这个方法也存在问题,解决如下(重点)
使用h5的plus方法,必须在uniapp项目内进行申明(这一点在uniapp官方文档有介绍)
即
// #ifdef APP-PLUS
H5代码
// #endif
plus.runtime.openURL()方法是打开手机自带的浏览器打开指定网页(电脑基座调试无效,必须手机基座调试)
getMsg(){
// #ifdef APP-PLUS
plus.runtime.openURL('https://aaaa/bb.action')//.action结尾的网址
// #endif
}
但是使用了这个方法还是没办法跳转,原因是配置文件没有写好跳转必须的条件
在manifest.json文件中,在打包配置中加上
"schemes" : [ "test" ]//测试
加上之后,在手机基座上调试就可以实现任何形式正确安全的网页的跳转
其他形式的网页需要在app内部进行访问,所以还是需要使用webview标签,但是直接写webview会有很多的不方便,所以可以新建一个文件,专门存放需要跳转的网页,再通过主页面传递url地址,就可以实现跳转
uni.navigateTo({
url:'./webView?url=' + 'https://aaaa/bbb.aspx',//传递参数的形式和接受方法uniapp中有介绍(目的页面即./webView,在onload中接受传递过去的参数)
})
如:
onLoad(res) {
this.url = res.url;//res.url就是传递过来的地址
}
uniapp获取设备的MAC地址
uniapp内没有获取设备信息的api,所以,只有靠万能的h5来获取设备的信息,基本信息都很简单,加上编译条件后,直接调用api就完事,但是要获取mac地址,却没有专门的api,如下获取mac地址,uniapp和h5通吃
//获取mac地址
var net = plus.android.importClass("java.net.NetworkInterface")//调用java的库
var wl0 = net.getByName('wlan0')
var macByte = wl0.getHardwareAddress()
var mac = '';
for (var i = 0; i < macByte.length; i++) {
var tmp = "";
var num = macByte[i];
if (num < 0) {
tmp =(255+num+1).toString(16);
} else {
tmp = num.toString(16);
}
if (tmp.length == 1) {
tmp = "0" + tmp;
}
mac += tmp;
//这里的mac的值就是用户的mac地址
顺便说一下,一般app都有启动页,所以上面所说的设备信息一般也是在启动页的时候获取,启动需要做的还有预加载第一个呈现在用户眼前的界面,以免数据获取延迟,体验不佳,当然还要异步获取用户的其他信息,比如位置信息等,放入缓存,以便后续直接获取
获取不同机型屏幕上方的custom(就是手机的展示信息,如时间,电量,信号,网速,还有摄像头等等)高度,以便自适应头部导航栏
方法一:
// colorui 获取设备屏幕大小,计算不同的custom(在APP.vue中的onLaunch函数中加入这个函数)
uni.getSystemInfo({
success: function(e) {
// #ifndef MP
Vue.prototype.StatusBar = e.statusBarHeight;
if (e.platform == 'android') {
Vue.prototype.CustomBar = e.statusBarHeight + 50;
} else {
Vue.prototype.CustomBar = e.statusBarHeight + 45;
};
// #endif
// #ifdef MP-WEIXIN
Vue.prototype.StatusBar = e.statusBarHeight;
let custom = wx.getMenuButtonBoundingClientRect();
Vue.prototype.Custom = custom;
Vue.prototype.CustomBar = custom.bottom + custom.top - e.statusBarHeight;
// #endif
// #ifdef MP-ALIPAY
Vue.prototype.StatusBar = e.statusBarHeight;
Vue.prototype.CustomBar = e.statusBarHeight + e.titleBarHeight;
// #endif
}
})
//在其他界面直接使用this.CustomBar即是屏幕上方的custom的高度,再结合实际代码就解决头部导航栏自适应的问题了,但是这是colorui中的方法,所以灵活性不高,建议方法二
方法二:
使用css变量(官方有自带的直接后去高度的css变量)
.headerCustom{
height: var(--status-bar-height);//这是官方的css变量,用于获取不同手机型号上方状态栏的高度
}
相对于第一种,更简便而且灵活,自己设计头部导航栏,不香嘛
项目中有一个地方用到了简单的三级联动,但是因为是用来插件,所以不太方便展示,下篇博文出一个三级联动的demo,此处仅附带demo图片
整个项目,主要需要我做两块,一块是后台管理的部分(自适应APP端),一部分是APP部分,是一个前后端分离的项目,同时也是我上手学vue一段时间后的第一个项目,其中有很多不足,大多数时间都是在搬砖,但是还是领悟了一点点前后端分离的概念。
首先,前后端分离不是说前端和后端完全只是管自己的那一部分,是逻辑上的分离,不表示前后端人员的分离,如交流接口等,逻辑上分离是指前端只需要告诉后端需要什么类型的什么数据,基本就可以开始写页面了,等待后端写好接口,通过请求将数据渲染到页面上就OK了,后端也是只需要了解到需要写什么接口,链接数据库,写数据接口和接口文档应该就可以了,当然后端在必要的时候处理应该处理的业务逻辑,在前端尽量少的处理逻辑,逻辑处理能交给后端的就交给后端,确实是前端需要处理的逻辑,才由前端来写。
其次,前端方面,需要写好请求的封装函数,方便调用而且可以提高效率、减少代码的冗余,这次后台管理使用的是axios请求,封装好了axios,只需要写调用接口的函数就可以实现前后端链接了,需要注意跨域的问题;APP使用的是promise封装uniapp自带的request请求,封装完成之后也是同样的套路,由于知识浅薄,封装方法都是嫖自于百度。
最后,整体项目还未完成,但是我退出了(和老师提前协商好了的),因为我在做项目的过程中发现虽然我可以学到很多前后端链接的知识,但是学到的基础知识基本没有,大部分都是百度找方法,搬砖搬砖搬砖(使用组件库和各种插件),所以我想退出来,好好沉淀一下学习更多的知识,以便后续能更好的进行实战,当然也不是完全放弃这个项目,只是不参与这个项目开发了而已,之前写过的代码仍然是我维护。
个人总结:学的太少,基础不是很扎实,算法思想不强,vue基础一般,学到了前后端的连接原理,了解了一个全栈项目是如何运行的,通过项目发现自我、认识自我,真的很有用,那样也不会一度膨胀,浪费韶华。
学的越多,没学的越多,正如你知道的越多,不知道的越多!