基础
template标签里,一定要有一层父级元素包裹住内容,不一定得是div标签,也可以是其他的,否则会报错
style标签里写样式时,加上scoped,则当前样式只适用于局部这个vue组件,否则适用于全局
vue一般是用数据驱动视频渲染,即很少在html中书写中文,一般是在data的return中定义数组,然后在html中用v-for来遍历数组以渲染json对象
mounted(){}里面写的东西,是挂载完成以后自动执行的
当使用export default时,import时不加花括号;使用export时,import时要加花括号
class属性妙用
什么时候绑定class属性:例如以下情况,:class="{ 'active' = isActive }"
(在style中写.active的样式,若isActive为true则class就会为active,然后该标签的内容会被样式渲染;冒号即为v-bind的缩写),为什么说这里是绑定呢,因为在data的return里写了isActive=true,然后在html里用了这个值,实现动态绑定效果
一个进阶一点的用法如下 :class="{ 'active' : isActive == index }" v-for="(item,index) in array"
然后在data的return里写isActive: 1 那么只有array数组中的第2个item元素会被active样式所渲染
再给一种用法 v-for="(item,index) in array" :class="{'active': item.current}"
然后在array数组里面给每一个item按是否要渲染写上它们的current值,array:[{txt:'登录',current:true},{txt:'注册',current:false}]
再给一种用法 v-for="(item,index) in array" :class="{'active': item.current}" @click="toggleMenu(item)"
然后在methods里面写toggleMenu(data){this.array.forEach( elem => {elem.current = false}); data.current = true}
这里有两个要注意的点,一是我们在html里面把当前的item传进来,然后在method中用data接收,此时传进来的是地址,即我们在method中改变data的current值,是可以影响到数组里面item的值的;二是注意这个toggleMenu方法的两个步骤,第一步我们用一个forEach方法,把array数组中所有对象的current值初始化为false,然后第二步再给当前传进来的这个item赋true值,以此达到效果点击哪个item它就被渲染,而且是可以不断改变选择的
在哪里写js方法
在utils(存放工具类函数)文件夹里创建validate.js文件,然后在里面写上stripscript(str){……}方法用于过滤特殊字符,注意要在该方法前加上export将方法暴露出去,这样当我们在某个vue文件里写校验时要用到这个方法,就在script标签里写上import{ stripscript } from '@/utils/validate.js'
,然后就可以直接用该方法了
这里有个注意的点,@在路径里代表什么呢,可以在vue.config.js(一个可选的配置文件)里面找,里面alias写了各种简写符号代表的是什么路径,发现@表示当前的src;拓展一下,配置文件里面的extensions可以自动添加文件名后缀,因此上面引用就可以简写为import{ stripscript } from '@/utils/validate'
也不会报错;再拓展一下,修改完vue.config.js一定要把项目重启一遍才会生效
resolve: {
extensions: [".js", ".vue", ".json"], //文件优先解析后缀名顺序
alias: {
"@": path.resolve(__dirname, "./src"),
"@c": path.resolve(__dirname, "./src/components"),
"@v": path.resolve(__dirname, "./src/views"),
"@u": path.resolve(__dirname, "./src/utils"),
"@s": path.resolve(__dirname, "./src/service")
}, // 别名配置
plugins: []
}
v-if与v-for妙用
v-if 为false时,直接删除DOM元素(这里要区别于v-show,它只是在元素中添加display,隐藏DOM元素);注意,当DOM元素中有接口时,v-if为true会请求接口
下举一个v-show的例子 在html的标签中,用上v-show="model === 'register'" v-for="(item,index) in array" @click="toggleMenu(item)"
然后在data的return里写上 model : 'register',array:[{txt:'登录',current:true,type:'login'},{txt:'注册',current:false,type:'register'}]
在methods中写 toggleMenu(data){ this.model=data.type }
注意,标签是否显示取决于模块值model是否为register,且默认显示;当发生点击事件,模块值发生改变,达到隐藏该标签的效果
vue3.0语法
在vue3.0的语法中,我们把data数据、生命周期、自定义的函数全部写在setup方法里面了
setup(props,context){
context.attrs
context.slots
context.parent
context.root
context.emit
//而在2.0的语法中,我们会写成this.$attrs
}
比如说上文我们在data的return中声明了一个对象 array数组,基础数据类型 model,那么现在我们就把它们的声明写在setup方法里面
setup(){
const array = reactive([
{txt:'登录',current:true,type:'login'},
{txt:'注册',current:false,type:'register'}
])
const model = ref('register')
}
注意这里的reactive方法,只要是声明一个对象的话,都必须用该方法处理;若声明的是基础类型变量,则用ref方法处理;还有,必须在script里写上,import { reactive,ref } from '@vue/composition-api'
;还有一个要注意的点,要取到基础类型数据的值,必须写model.value才可以,比如model.value = 10
,对象类型不需要哦,只要写对象变量名即可
拓展: console.log(isRef(model) ? true : false)
用于判断是否为ref类型
函数同样在setup里面声明
setup(){
const toggleMenu = (data => {
array.forEach( elem => {
elem.current = false
}); //注意这里,3.0中没有this的写法了,直接写变量名即可
data.current = true
})
}
注意这样还没完,最后一步,把声明的要在模板中使用的data,生命周期,函数等等全都return出去
setup(){
return{
array,
model,
toggleMenu
}
}
axios
首先,下载axios依赖,然后在script中写上import axios from ‘axios’
在网页的console控制台里,选择network,选择XHR,就可以看到我们的请求
我们在上文写到@click="toggleMenu(item)
,然后我们在toggleMenu方法里用上axios,当点击时就会去请求接口
const toggleMenu = (data => {
axios.get('/user?ID=12345')
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
array.forEach( elem => {
elem.current = false
});
data.current = true
})
注意,无论你是用get,post,delete等等各种方法请求,最终都是来到axios.request;因此我们可以全都用request方法来写,然后在method中进一步选择get,post等等,如下
const toggleMenu = (data => {
axios.request({
// `url` 是用于请求的服务器 URL
url: '/user',
// `method` 是创建请求时使用的方法
method: 'get', // default
})
})
接口的复杂写法
我们在login文件夹里写了index.vue,然后在src中创建一个api文件,里面对应写一个login.js文件,里面就可以写上接口请求,以及引入拦截器,然后回到index.vue,我们在里面import {要用的请求方法名} from ‘@/api/login.js’
我们在vue文件里面调用了js文件里的接口,接口就会去调用拦截器,开始拦截事件处理,拦截器处理完信息后,就会把信息全都返回到vue文件里
注意,拦截器是在request.js里面定义的,接下来我们要自己创建一个axios拦截器,在utils文件夹里创建request.js文件,在里面贴上”创建变量axios,赋给变量instance“的语句,然后贴上文档提供的拦截器写法格式时,注意一定要把axios改成我们自己创建的变量的名字instance
import axios from 'axios'
const instance = axios.create();//创建变量axios,赋给变量instance
// 添加请求拦截器
instance.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
// 添加响应拦截器
instance.interceptors.response.use(function (response) {
// 对响应数据做点什么
return response;
}, function (error) {
// 对响应错误做点什么
return Promise.reject(error);
});
export default instance
这里讲一个点:我们在request.js里面写export default instance
,然后在api里面的js文件里面写import service from '@/utils/request'
,然后底下的接口写
service.request({
url: '/user',
method: 'get',
})
这是完全没毛病的,我们在request.js中不过是把这个对象暴露出来,然后我们在api的js文件里把这个对象引用进来,并且重命名了一次
以下给个接口实例:
在src/api/user.js里我们写接口
import request from '@/utils/ysw-request'
// 查询客户列表
export function listUser(query) {
return request({
url: '/user/listDetail',
method: 'post',
params: query
})
}//这里是声明了一个listUser方法,里面返回一个接口请求,并把这个方法暴露出去
// 获取地区信息
export function listArea() {
return request({
url: '/region/info',
method: 'get'
})
}
然后在src/views/user的各个vue文件里,我们就可以调用它们了
import {
listArea,
listUser,
} from '../../../api/user'
methods: {
getList() {
this.listLoading = true
this.listQuery.provinceCode = this.areaCode[0]
this.listQuery.cityCode = this.areaCode[1]
this.listQuery.districtCode = this.areaCode[2]
let query = JSON.parse(JSON.stringify(this.listQuery))
if(query.subject.length !== 1) delete query.subjectScore
query.subject = query.subject.join(',')
if(query.subjectScore === '') delete query.subjectScore
listUser(this.listQuery).then(res => {
const data = { res }
this.listLoading = false
this.list = data.res.data
})
}
}
以下重点写一下拦截器的写法,首先必须了解一下vue的环境变量
在项目根目录中放置下列文件来指定环境变量
.env # 在所有的环境中被执行
.env.local # 只在本地环境上做测试,不会被提交到代码仓库上去
.env.[mode] # 只在指定的模式中被载入
.env.[mode].local # 只在指定的模式中被载入,但会被 git 忽略
再来讲三种模式
development 模式 用于 vue-cli-service serve,即我们的开发模式
test 模式 用于 vue-cli-service test:unit,即我们的生产模式,已经发布在线上可以使用的模式
production 模式 用于 vue-cli-service build 和 vue-cli-service test:e2e,测试模式,用得少
对于模式我们要记住几个点,一个模式可以包含多个环境变量
怎么声明?通过.env文件加后缀来声明,比如说,我们在根目录创建一个.env.development的文件,那么在这个文件里面声明的变量就只会在development模式下被载入;比如说,vue-cli-service build 会加载可能存在的 .env、.env.production 和 .env.production.local 文件然后构建出生产环境应用
还有,模式的名称是有标准写法格式的,NODE_ENV的值,就是用来决定你运行的模式是开发、生产、还是测试的,在 production 模式下被设置为 “production”,在 test 模式下被设置为 “test”,默认则是 “development”
还有,我们在每个模式里是默认就有这个NODE_ENV的值的
在vue.config.js里面,我们可以把这个值打印出来看
module.exports = {
publicPath: process.env.NODE_ENV === "production" ? "/" : "/"
}
举例子:
.env.development里面声明了以下环境变量,这是在开发环境下会被执行的文件
# 开发环境配置
ENV = 'development'
# 时肆教育管理系统/开发环境
VUE_APP_BASE_API = '/occam-api'
# 路由懒加载
VUE_CLI_BABEL_TRANSPILE_MODULES = true
.env.production里面声明了以下环境变量,这是在生产环境下会被执行的文件
# 生产环境配置
ENV = 'production'
# 时肆教育管理系统/生产环境
VUE_APP_BASE_API = '/occam-api'
这里要注意一个点,环境变量的命名规范,前面的VUE_APP是固定的,后面加的_BASE_API才是我们自定义的名称
如何打印出来看值?
console.log(process.env.NODE_ENV)//打印出来development
console.log(process.env.VUE_APP_BASE_API)
这样分文件定义环境变量有什么用处呢,同一个变量我们在两个环境的文件里可以取不同的值,那么我们发布上线,或者平时测试,系统就会自动去切换这个变量的值了,我们用它的变量名即可
说完模式与环境变量,接下来来讲拦截器
第一步:自定义axios拦截器,配置baseURL,即前端API地址
const instance = axios.create({
baseURL: 'https://some-domain.com/api/',
timeout: 1000,
headers: {'X-Custom-Header': 'foobar'}
});
给个项目实例
const service = axios.create({
// axios中请求配置有baseURL选项,表示请求URL公共部分
// baseURL: process.env.VUE_APP_BASE_API,
baseURL: config.host,
// 超时
timeout: 60000
})
然后我们看到config.js里面写到
export default {
host : 'https://dev.api.wx.occamedu.com/occamapi'
// host: 'https://test.api.wx.occamedu.com/occamapi',
// host: 'https://api.wx.occamedu.com/occamapi',
}
这样根据注释去换host的值,我们就可以请求到不同模式的地址了
第二步:解决跨域问题,即在vue.config.js里面配置proxy
在网上搜索axios 跨域,我们得到以下代码:
proxyTable: {
//axios跨域改造 by zhengkai.blog.csdn.net
'/api': {
target:'http://localhost:8888/cert/', // 你请求的第三方接口
changeOrigin:true, // 在本地会创建一个虚拟服务端,然后发送请求的数据,并同时接收请求的数据,这样服务端和服务端进行数据的交互就不会有跨域问题
pathRewrite:{ // 路径重写,
'^/api': '' // 替换target中的请求地址,也就是说/api=/target,请求target这个地址的时候直接写成/api即可。
}
}
},
我们把baseURL粘贴到target里面,作为API服务器的地址
拓展一个小知识,当我们在正式服上面运行时,该地址后面会系统自带一个/api,这样就会与我们在跨域里写的’/api’名称搞乱,这里我们自己起的的/api等价于target的地址,所以一般会自己改个名字,比如说改成devApi
捋一下这里的逻辑,我们自己起的’/api’ === ‘http://localhost:8888/cert/’(target里的地址)
给个例子,我们在request.js里面写拦截器
const BASEURL = process.env.NODE_ENV === 'production' ? '' : '';
const service = axios.create({
baseURL: BASEURL,
timeout: 60000
})
这样子跑出来baseURL是本地端口/api,这里的/api等价于target的地址
即http://192.168.0.106:8080/api/ = ‘http://localhost:8888/cert/’
那么底下pathRewrite的’^/api’是干什么用的,是转向用的,它是一个正则表达式,表示查找我们本地服务运行起来时的api字符串,并将其用后面的值覆盖掉
再给项目实例
proxy: {
// detail: https://cli.vuejs.org/config/#devserver-proxy
[process.env.VUE_APP_BASE_API]: {
// target: `https://secretary.occamedu.com/occam-api`,
target: `https://dev.secretary.occamedu.com/occam-api`,
// target: `https://test.secretary.occamedu.com/occam-api`,
// logLevel: 'debug',
// target: `http://localhost:9001/occam-api`,
changeOrigin: true,
pathRewrite: {
['^' + process.env.VUE_APP_BASE_API]: ''
}
}
},
拓展一下,拦截器这里的timeout: 60000,意思是当我们网络请求接口超过6秒中还没成功,那么将请求失败,执行Promise.reject
讲一下Promise
写法如下:(resolve,reject)这是作为两个参数
let promise = new Promise((resolve,reject) => {
})//声明完,我们就可以回调两个常见方法
promise.then(response => {
}).catch(error => {
})
最简单的理解,当我们Promise这边调用resolve()方法时,底下promise就回调.then(response => {})这一块,同理当Promise这边调用reject()方法时,底下promise就回调.catch(error => {})这一块,一一对应
举个例子
let promise = new Promise((resolve,reject) => {
reject()
})
promise.then(response => {
console.log("成功")
}).catch(error => {
console.log("失败")
})
明显跑起来后控制台就会出现失败二字
let promise = new Promise((resolve,reject) => {
resolve(123)
})
promise.then(response => {
console.log("成功")
console.log(response)
}).catch(error => {
console.log("失败")
})
传入参数后,response将会接受到值,打印出123
进阶一下,声明一个psomise方法
function psomise(status){
return new Promise((resolve,reject) => {
if(status) {
console.log("promise成功")
resolve("promise返回数据成功")
}
if(!status) {
console.log("promise失败")
resolve("promise返回数据失败")
}
})
}
我按以下代码回调,控制台将出现promise成功
promise(true).then(response => {
}).catch(error => {
})
我按以下代码回调,控制台将出现promise成功 promise返回数据成功
promise(true).then(response => {
console.log(response)
}).catch(error => {
console.log(error)
})
再进阶一下,写一个链式的调用,我们定义promise1和promise1两个funuction
function psomise1(status){
return new Promise((resolve,reject) => {
if(status) {
console.log("第一个promise成功")
resolve("第一个promise返回数据成功")
}
if(!status) {
console.log("第一个promise失败")
resolve("第一个promise返回数据失败")
}
})
}
function psomise2(status){
return new Promise((resolve,reject) => {
if(status) {
console.log("第二个promise成功")
resolve("第二个promise返回数据成功")
}
if(!status) {
console.log("第二个promise失败")
resolve("第二个promise返回数据失败")
}
})
}
然后我们按以下代码回调,控制台显示第一个promise成功 第一个promise返回数据成功 第二个promise成功
promise1(true).then(response => {
console.log(response)
promise2(true)
}).catch(error => {
console.log(error)
})
然后我们按以下代码回调,控制台显示第一个promise成功 第一个promise返回数据成功 第二个promise成功 第二个promise返回数据成功
promise1(true).then(response => {
console.log(response)
promise2(true)
}).then(response => {
console.log(response)
}).catch(error => {
console.log(error)
})
这样当我们要写很多方法嵌套来嵌套去的时候,我们在声明function时可以独立分开声明,我们在执行回调时使用promise来链式使用,达到分离效果
了解完promise的回调基础后,我们再来讲拦截器里面的细节
// 添加请求拦截器
axios.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
// 添加响应拦截器
axios.interceptors.response.use(function (response) {
// 对响应数据做点什么
return response;
}, function (error) {
// 对响应错误做点什么
return Promise.reject(error);
});
当我们写以下代码时,接口响应传进来的数据全部在控制台打印出来//我们在服务器请求了相关的数据回来之后,在响应拦截这里做一些事情
axios.interceptors.response.use(function (response) {
console.log(response)
return response;
}, function (error) {
return Promise.reject(error);
});
我们也可以这么写
axios.interceptors.response.use(function (response) {
let data = response.data
if(data.resCode !== 0){
console.log("有错")
return Promise.reject(data);
}
else{
return response
}
return response;
}, function (error) {
return Promise.reject(error);
});
我们在服务器请求了相关的数据回来之后,在响应拦截这里若出错了,返回Promise.reject(data),然后返回到api的方法接口那里,然后返回到vue那里,我们在vue那个方法接口那里,用上.then与.catch,就可以把参数回调进来在前台显示
给个实例:(GetSms是个接口名,root.$message是el组件)
GetSms(requestData).then(response =>{
let data = response.data
root.$message({
message: data.message,
type: 'success'
}).catch(error =>{
})
})
前面拓展过timeout,这里讲一下setTimeOut方法,其实就是一个时间延时效果,先延迟自定义的时间,再执行一次方法(注意这里的一次,区别于setInterval,它会隔一段延迟的时间,就执行一次,只有在某个条件下才会停止)
setTimeOut(() => {
GetSms(requestData).then(response =>{
let data = response.data
root.$message({
message: data.message,
type: 'success'
}).catch(error =>{
})
})
}, 300)
//实现延迟3毫秒
然后讲一下停止定时器的方法,对应clearTimeOut(定时器变量名称)和clearInterval(变量)
这里我们来写一个倒计时的方法
const countDown(() => {
setTimeout(() => {
console.log(一秒后只执行一次)
},1000)
setInterval(() => {
console.log(每隔一秒执行一次)
},1000)
})
先声明好timer,用的时候传60s进去,countDown(60)
const countDown((number) => {
let time = number
timer.value = setInterval(() => {
time--
console.log(time)
if(time === 0){
clearInterval(timer.value)
}else{
codeButtonStatus.text = '倒计时${time}秒' //注意这里等价于‘倒计时’+time+‘秒’,不过是es6的写法而已,简便建议多用!
}
},1000)
})
如何联调
前后端同时打开服务器,然后连在同一个局域网上
前端拦截器处,把baseURL改为后端的ip地址,再加上端口号,然后就可以在不部署接口到服务器的情况下进行接口请求了
举个例子:
就是这个baseURL
前端post请求get请求内–body和query传参的区别
https://www.cnblogs.com/chr506029589/p/12987356.html
传的时候看清楚后端要的是body还是query
前后端敏感数据加密
https://www.cnblogs.com/king94Boy/p/12987905.html