1.项目文件vueCli3.0下:
pulic文件下:favicon.ico是网址上方标题的的小图标。
index.html:是入口文件。
src:项目的主文件:
assets:是存放静态资源(如,图片,css等)的文件。
img:存放图片文件。
iconfont:图标字体文件。
css:存放公共css文件。
js:存放第三方js文件。
components:可复用的组件。
views:页面组件。
api:项目的ajax请求请求。
config:项目的配置文件。
index.js:配置地址,在需要的地方import config from "./config/index.js"引入配置对象即可。
directive:存放vue的自定义指令。
index.js:
lib:
tools.js:存放与业务无关的,纯粹的工具方法,如封装的通用js方法。
util.js:存放与业务结合有关的,如定义的路径变量。
router:存放路由有关的,将router.js移入该文件内。
index.js::存放引入文件,配置路由实例。如下图:
import Vue from 'vue'
import Router from 'vue-router'
import routes from "./router.js"
import {setTitle} from "@/lib/util.js"
Vue.use(Router)
const router=new Router({
routes
})
const IS_LOGIN=true //根据存储在cookie的登录信息判断是否登录的判断
router.beforeEach((to,form,next)=>{ //router实例的beforeEach方法是注册一个全局前置守卫,从from路由对象到to路由对象,即禁止在没有登录情况下,在网址栏输入admin会跳转到admin页面。
to.meta && setTitle(to.meta.title) //设置页面网址上面的标题
if(to.name !=="login"){ //如果即将跳转的页面不是登录页面,如跳转的是admin页面
if(IS_LOGIN) { //根据是否已经登录,判断是否可以跳转到adminy页面
next() //如果已即登录,就直接跳转
}else{
next({name:'login'}) //如果没有登录,就跳转到登录页面
}
}else{ //如果即将跳转的页面是登录页面
next()
}
})
router.beforeResolve((to,form,next)=>{ //router实例的beforeResolve方法是注册一个全局守卫,从from路由对象到to路由对象,即页面跳转前所有钩子执行完最后执行该函数 ,
})
router.afterEach((to,form)=>{ //router实例的afterEach方法是注册一个全局后置守卫,从from路由对象到to路由对象,即页面跳转之后执行,
//loading=false
})
export default router
/**
* 1. 导航被触发
* 2. 在失活的组件(即将离开的页面组件)里调用离开守卫 beforeRouteLeave
* 3. 调用全局的前置守卫 beforeEach
* 4. 在重用的组件里调用 beforeRouteUpdate
* 5. 调用路由独享的守卫 beforeEnter
* 6. 解析异步路由组件
* 7. 在被激活的组件(即将进入的页面组件)里调用 beforeRouteEnter
* 8. 调用全局的解析守卫 beforeResolve
* 9. 导航被确认
* 10. 调用全局的后置守卫 afterEach
* 11. 触发DOM更新
* 12. 用创建好的实例调用beforeRouterEnter守卫里传给next的回调函数
*/
router.js::单独存放处理路由文件,配置路由列表。如下图:
import Home from './views/Home.vue'
export default [
{
path: '/',
alias:'/home_page', //为路由取别名,即访问"/home_page"和访问"/"和通过name:"home",访问的效果是一样的
name: 'home',
component: Home, //普通写法
beforEnter:(to,from,next)=>{ //该组件独有的守卫
if(from.name="login"){
alert("这是从登陆页面跳转过来的")
}else{
alert("这不是从登陆页面跳转过来的")
}
next() //一定要在最后执行next(),否则不会跳转
}
},
{
path: '/about',
name: 'about',
component: () => import('./views/About.vue') //这样写,有懒加载的作用,即该页面显示时才会加载。
},
{
path: '/argu/:name', //动态路由传参:高级写法,动态加载路由,name是参数
props:true, //表示允许组件props:{}中接受name参数值,然后可以直接渲染在页面,{{name}},或者$route.params.name获取路由参数
component: () => import('./views/Argu.vue')
},
{
path: '/parent', //嵌套路由
component: () => import( './views/Argu.vue'),
children:[
{
path:"child1", //注意只有父级需要'/'
component: () => import('./views/child1.vue'),
},
{
path:"child2",
component: () => import('./views/child2.vue'),
},
]
},
{
path: '/name_router', //命名视图
components: {
default:() => import('./views/about.vue') , //如果 没有name属性值,默认对应该路由
email:() => import('./views/parent.vue') , // 对应该路由
tel:() => import('./views/argu.vue') // 对应该路由
}
},
{
path: '/home', //重定向路由
redirect:"/", //3种重定向的方法
// redirect:{
// name:"home1"
// },
// redirect:to =>{ //如根据参数确定跳转到哪个页面
//// console.log(to)
//// return "/"
// return {
// name:"home1"
// }
// }
},
{
path: '/login',
component: () => import('./views/login.vue')
},
{
path:"*", //404页面一定要写在最后,因为是从上到下匹配路由。
component: () => import('./views/error404.vue'),
}
]
store::存放状态管理的文件。store.js移入到该文件内,修改为index.js。记得修改main.js的入口文件
index.js:
mutations.js:
getters.js: 相当于计算属性,对state参数的计算
state.js:
actions.js:
mudels:存放模块文件。
user.js:如存放用户的登录信息
mock: 模拟返回的数据,在后台接口还么有完成,前端通过模拟数据。参考:http://mockjs.com/
vue.config.js:自定义设置文件的配置,如跨域请求地址,配置后台(相当于vue-cli3.0以下版本的build/serve-dev-serve.js文件),路径简写src为@,设置项目的基本路径。
2. vscode编译器的:添加配置编译器的文件:
在src同级下.editorconfig:
root=true
[*] //表示该编译器配置针对所有文件
charset=utf-8
indent_style=tabs 缩进键
indent-size=2 缩进的大小
然后在编译器中安装editorConfig for VS Code,然后就可运行添加的配置编译器的文件了
3. router-link与router-view:router-link是封装了的a标签。router-view渲染路由视图,两者效果一样。
命名路由:即路由有name属性name: 'home',
3.1 配置路由列表的5种方法:
a.为路由取别名:
import Home from './views/Home.vue' //如果有 name: 'home',表示是命名路由。
{
path: '/',
alias:'/home_page', //取别名,即访问"/home_page"和访问"/"和通过name:"home",访问的效果是一样的
name: 'home', //命名路由
component: Home //普通加载页面模块
},
b.页面懒加载页面模块:即该页面显示时才会加载。不需要像import Home from './views/Home.vue'一样提前加载。
{
path: '/about',
name: 'about',
component: () => import('./views/About.vue')
},
c.动态路由加载传参:动态加载路由,name是参数,{{$route.params.name}}调用参数name的值。
{
path: '/argu/:name',
props:true, //表示允许组件props:{}中接受name参数值,并直接渲染在页面{{name}}
name: 'argu',
component: () => import( './views/argu.vue')
},
d.嵌套路由: 。
{
path: '/parent', //嵌套路由
component: () => import('./views/Argu.vue'),
children:[
{
path:"child1", //注意只有父级需要'/'
component: () => import( './views/child1.vue'),
},
{
path:"child2",
component: () => import('./views/child2.vue'),
},
]
}
e. 命名视图:同时存在多个路由跳转时 。
{
path: '/name_router',
components: {
default:() => import('./views/about.vue') , //如果 没有name属性值,默认对应该路由。
email:() => import('./views/parent.vue') , // 对应该路由。
tel:() => import('./views/argu.vue') // 对应该路由。
}
},
f. 重定向:重新定义跳转的路径,如本来是访问 '/home',重新绑定跳转到"/"。
{
path: '/home',
redirect:"/", //3种重定向的方法
// redirect:{
// name:"home1"
// },
// redirect:to =>{ //如根据参数确定跳转到哪个页面
//// return "/"
// return {
// name:"home1"
// }
// }
}
4. js操作路由(即编程式的导航)进行页面跳转:
a. 返回/前进一页:返回:this.$router.go(-1)、this.$router.back()。前进:this.$router.go(1)。
b. 跳转到其他页:
this.$router.push("/parent")。
this.$router.push({name:"parent",query:{name:"ace"}),即浏览历史纪录保存着,query是参数。
this.$router.push({path:`/argu/${name}`}) ,es6带参数跳转,针对 path: '/argu/:name',该路由。
this.$router.push({path:"/parent",params:{name:"ace"}) , 带参数跳转。
c. 用其他页替换本页:this.$router.replace("/about")或this.$router.replace({name:"parent"}),即浏览历史纪录没有了。
5. 路由传值:
5.1 基于动态路由的页面(path: '/argu/:name')传值。
{
path: '/argu/:name',
props:true, //表示允许Argu.vue组件中props:{}中接受name参数值,然后可以直接渲染在页面{{name}}
component: () => import( './views/argu.vue' )
},
5.2 基于普通页面传参,对象模式传参。
{
path: '/about',
props:{
food:"香蕉"
}, //表示允许about.vue组件中props:{}中接受food参数值,然后可以直接渲染在页面{{food}}
component: () => import( './views/argu.vue')
},
5.3 基于普通页面传参,函数模式传参。
{
path: '/parent',
props: route=>{
return {
food:route.query.food
}
}, //表示允许parent.vue组件中props:{}中接受food参数值,然后可以直接渲染在页面{{food}}
component: () => import( './views/argu.vue')
}
6. 导航守卫:如根据是否登录或登录者的权限跳转不同的页面。
6.1 全局守卫:即在全局设置一个守卫。在router/index.js中配置全局守卫
const router=new Router({
routes
})
const IS_LOGIN=true //是否登录的判断
router.beforeEach((to,form,next)=>{ //router实例的beforeEach方法是注册一个全局前置守卫,从from路由对象到to路由对象,
if(to.name !=="login"){ //如果即将跳转的页面不是登录页面
if(IS_LOGIN) {
next() //如果已即登录,就直接跳转
}else{
next({name:'login'}) //如果没有登录,就跳转到登录页面
}
}else{ //如果即将跳转的页面是登录页面
if(IS_LOGIN) {
next({name:'home'}) //如果已即登录,就直接跳转首页,{name:'home'}也可是'/home'
}else{
next() //如果没有登录,就直接跳转
}
}
})
router.beforeResolve((to,form,next)=>{ //router实例的beforeResolve方法是注册一个全局守卫,从from路由对象到to路由对象,即页面跳转前所有钩子执行完最后执行该函数
})
router.afterEach((to,form)=>{ //router实例的afterEach方法是注册一个全局钩子,从from路由对象到to路由对象,即页面跳转之后执行,
//loading=false
})
6.2 组件独享守卫:即该组件独有的守卫。 如 在router/router.js中path:"/"中配置组件守卫 。
{
path: '/',
component: Home, //普通写法
beforEnter:(to,from,next)=>{ //该组件独有的守卫
if(from.name="login"){
alert("这是从登陆页面跳转过来的")
}else{
alert("这不是从登陆页面跳转过来的")
}
next() //一定要在最后执行next(),否则不会跳转
},
6.3 在组件里面的3种守卫,如在login.vue组件里面与生命周期同级:
#a.beforeRouteEnter:即将跳转到当前页面,但是页面还没有渲染,所有里面的this不指向实例vue该组件
beforeRouteEnter(to,from,next){
console.log(to.name)
next(vm=>{
console.log(vm) //而这里的vm就是该组件的实例了。
})
}
#b.beforeRouteLeave:即将离开当前页面时执行,如即将离开编辑页面,弹出提醒框,提醒你是否保存编辑内容。
beforeRouteLeave(to,from,next){ //此时组件是已经渲染了,this可以执行vue实例
const leave=confirm("你确定要离开本页面么?")
if(leave){
next()
}else{
next(false)
} //false表示不发生页面跳转
}
#c.beforeRouteUpdate:即路由发生改变,组件(复用组件)被复用时,执行。如同一个页面,在url上修改了参数之后,该页面被复用了,就会执行
beforeRouteUpdate(to,from,next){ //此时组件是已经渲染了,this可以执行vue实例
console.log(to.name)
}
注意:整个导航守卫的流程:
a. 导航被触发:即url路由地址发生改变。
b. 在失活的组件:即将离开的页面组件,里调用离开守卫函数(beforeRouteLeave)。
c. 调用全局的前置守卫:即函数beforeEach。
d0. 如果跳转的是重/复用的组件里调用:在复用/重用的组件里调用函数beforeRouteUpdate。
d1. 如果跳转的是新的组件调用:在新的组件里调用beforeRouteEnter。
e. 调用路由独享的守卫:即router/router.js里面的函数beforEnter 。
f. 解析异步路由组件:
g. 在被激活的组件里(即将进入的页面):调用beforeRouteEnter。
h. 调用全局的解析守卫:即调用beforeResolve。
i. 导航被确认。
j. 调用全局的后置守卫:afterEach。
k. 触发DOM的渲染。
l. 用创建好的实例调用beforeRouteEnter守卫传给next()的回调函数。
7. 通过页面组件的meta:字段设置每个页面的window.document.title标题。
7.1 如在login.vue组件中:
export default{
meta:{ //与生命周期平级。
title:"我是登录页面标题"
}
}
7.2 然后在router/index.js的beforeEach中设置 to.meta.title && setTitle(to.meta.title),import {setTitle} from "@/lib/util.js"
router.beforeEach((to, from, next) => {
//设置docutment.title的标题内容
to.meta && setMetaTitle(to.meta.title)
var IS_LOGIN=true; //是否登录的判断,getCookie("isLogin")可以有cookie中是否有登录信息决定
if(IS_LOGIN){
next()
}else{
if(to.name==="login"){
next()
}else{
next({name:'login'})
}
}
})
7.3 在lib/util.js中定义一个设置url上方的标题的函数。
export const setTitle =(title)=>{
window.document.title=title || "title不存在时的默认值"
}
8. 设置路由跳转时的动画效果:
8.1 静态设置动画效果:
/*一组路由视图,设置动画效果,必须写key值。然后在css中设置.slide-enter/leave-to/active样式*/
8.2 动态设置动画效果:
//一组路由视图,设置动画效果,必须写key值。然后在css中设置.router-enter/leave-to/active样式。
data(){
return{
transitionName:""
}
},
watch:{
"$route"(to){ //to表示当前页面,表示如果在url传入参数transitionName存在如值为"router",则将其赋值给this.transitionName
to.query && to.query.transitionName && (this.transitionName =to.query.transitionName)
}
}
9. 父组件与子组件的传值:
9.1 父传值给子组件:props传值
父组件:
子组件:
{{value}}
props:{
son:{
type:[String,Number],
default:"123"
}
}
9.2 子传值给父组件:自定义事件传值
子组件:
this.$emit("aceHandle",val)
父组件:
myHandle(val){
console.log("这是自定义事件传值",val)
}
9.3 父子组件之间相互调用彼此的方法:
父组件调用子组件的方法:this.$refs.son.方法名。
子组件调用父组件的方法:方法一:this.$emit("aceHandle",val)。方法二:this.$parent.方法名。
注意:在vue中@input="inputHandle" 表示input输入值改变时触发的内置事件。inputHandle(e){console.log("输入框的值是",e,target.val)}
10.bus传值,状态管理:bus即空的vue实例,如用于简单场景下的兄弟组件之间传值。
10.1 src下创建bus/index.js文件。
import Vue from "vue"
const Bus=new Vue()
export default Bus
10.2 在main.js引入bus,并添加到vue的原型对象里,然后在任何地方都可以不需要在引入,直接使用this.$bus.
import Bus from './bus/index.js'
Vue.prototype.$bus=Bus
10.3 从兄弟组件A传值给兄弟组件B:
A组件:
this.$bus.$emit("myHandle",val)
B组件:
mouted(){
this.$bus.$on("myHandle",(val)=>{ //监听自定义事件myHandle
//在这里处理接收到B组件传递过来的值
})
}
11. vuex传值,状态管理:src下创建store文件用于vuex状态管理
11.1 index.js入口文件的管理: 然后在main.js中引入import store from './store/index.js',然后全局设置:
import Vue from 'vue'
import Vuex from 'vuex'
import state from "./state" //全局状态参数管理
import getters from "./getters" //相当于计算属性,对state参数的计算
import mutations from "./mutations"
import actions from "./actions"
import user from "./mudule/user.js" //单独某个模块如用户模块user的状态管理
Vue.use(Vuex)
export default new Vuex.Store({
state , //es6语法相当于state:state
getters,
mutations ,
actions,
mudules:{
user
},
plugins:[saveInLocal] //加载插件
})
11.2 state.js全局状态参数管理:
定义全局参数:
const state={
appName:"我是全局参数,在组件内都可传递值"
}
export default state
调用全局参数值:
方法1:
在某个组件内{{this.$store.state.appName}}就可获取该值了,或通过可在该组件写入到计算属性中computed
{{appName}}
computed:{
appName(){
return this.$store.state.appName
}
}
方法2:
{{appName}}
import {mapState} from "vuex"
computed:{
...mapState(["appName"]) //这2种写法一样。...表示展开一个对象。
// ...mapState({
// appName:state=>state.appName
// })
}
11.3 user.js 获取模块中user.js里面的状态参数管理:
定义单独某个模块中全局参数:
const state={
userName:"我是user模块的参数值"
}
const mutations={}
const actions={}
export default{
state,
mutations,
actions
}
调用单独某个模块全局参数值:
方法1:
在某个组件内{{this.$store.state.user.userName}}就可获取该值了,或通过可在该组件写入到计算属性中computed
{{userName}}
computed:{
userName(){
return this.$store.state.user.userName
}
}
方法2:
{{userName}}
import {mapState} from "vuex"
computed:{
...mapState(["userName"]) //这3种写法一样。...表示展开一个对象。
// ...mapState({
// userName:state=>state.user.userName
// })
// ...mapState("user",{ //传入模块名
// userName:state=>state.userName
// })
}
注意:在模块状态管理中如果有命令空间,即
export default{
namespaced:true, //设置命名空间为true,使得模块更加密闭,不受到外界的干扰
state,
mutations,
actions
}
方法3:
{{userName}}
import {createNamespacedHelpers} from "vuex"
const {mapState}=createNamespacedHelpers("user") //参数user是命令空间的名称(模块名,user.js)
computed:{
...mapState(["userName"]) //这2种写法一样。...表示展开一个对象。
// ...mapState({ //不许要在传入模块名了
// userName:state=>state.userName
// })
}
11.4 getters.js相当于组件的computed计算属性,是对state状态的计算处理。 在模块如user.js中使用getters和使用state方法一样
定义getters(计算属性):
const getters={
appNameVersion:(state)=>{ //依赖于state.js
return state.appName+"v2.0"
}
}
export default getters
调用getters(计算属性的结果):
方法1:
在某个组件内{{this.$store.getters.appNameVersion}}就可获取该值了,或通过可在该组件写入到计算属性中computed
{{appNameVersion}}
computed:{
appNameVersion(){
return this.$store.getters.appNameVersion
}
}
方法2:
{{appNameVersion}}
import {mapGetters} from "vuex"
computed:{
...mapGetters(["appNameVersion"]) //这3种写法一样。...表示展开一个对象。
// ...mapGetters({
// userName:state=>state.appNameVersion
// })
}
11.5 mutations.js修改state状态参数的值;
在mutations定义修改state的事件:
import vue from "vue"
const mutations={
set_app_name(state,params){ //state表示store/state.js,params是要修改state状态中参数的新值,可能是对象,或字符串
state.appName=params //参数时字符串
//state.appName=params.appName //参数时对象
},
set_app_version(state){ //如果stata.js中没改属性参数,这个表示给state.js中添加version并赋值v2.0
vue.set(state,"version","v2.0")
}
}
export default mutations
在组件里调用上面定义的事件:
方法1:
this.$store.commit("set_app_name","我是state.js里新修改的appName的值") //参数是字符串
this.$store.commit("set_app_name",{appName:"我是state.js里新修改的appName的值"}) //参数是对象
this.$store.commit({type:"set_app_name",appName:"我是state.js里新修改的appName的值"}) //参数是对象,且事件也包含在对象里
方法2:
在组件的方法里
import {mapMutations} from "vuex"
methods(){
...mapMutations([
"set_app_name",
"set_app_version"
]),
Handle(){
this.set_app_version("newAppName");
this.set_app_name()
}
}
注意:mutations和getters和actions在模块里面是在模块里且没有命令空间限制,会默认将模块中的mutations和getters和actions注册在全局store文件下的mutations.js和getters.js和actions.js,如下写,且不需要传入模块名,如"user"
...mapMutations([
"set_app_name",
"set_app_version"
]),
但是如果有命令空间限制namespaced:true, //设置命名空间为true,使得模块更加密闭,不受到外界的干扰
...mapMutations("user",[ //需要传入模块
"set_app_name",
"set_app_version"
]),
11.6 action.js异步修改state.js的状态值,如通过获取后台数据,将state.js的值修改成后台获取的数据:
定义异步修改状态值的方法:
import {getAppName} from "@/api/app.js"
const action={ //异步操作状态的改变,如通过接受接口的api返回的数据,从而改变state.js的状态值
// updateAppName({commit}){ //es6写法:{commit},相当于func(obj){const commit=obj.commit}
// getAppName().then(res=>{
// console.log(res)
// const {info:{appName}}=res; //es6的
// commit("set_app_name",appName);
// commit("set_app_name",res.info.appName); //通过commit关联mutations.js中的是set_app_name()方法,从而异步修改state.js状态值
// }).catch(err){
// console.log(err)
// }
//}
async updateAppName({commit}){ //es8的语法,与上面执行结果一样,只是将异步变成同步。
try{
const {info:{appName}} =await getAppName()
commit("set_app_name",appName);
}catch(err){
console.log(err)
}
}
}
export default action
调用异步修改的状态值的方法:
方法1:
mport {mapAction} from "vuex"
methods(){
...mapAction([
"updateAppName"
]),
Handle(){
this.updateAppName()
}
}
方法2:
this.$store.dispatch("updateAppName",val)
11.7 在模块中定义 action方法
user.js中:
const state={}
const mutations={}
const actions={
updateUserName({commit,state,rootState,dispatch}){ //commit用于提交到mutation,state值当前文件下state,rootState值store文件夹下state.js
rootState.appName
}
}
export default{
state,
mutations,
actions
}
11.8 api/app.js模拟后台接口返回数据:
export const getAppName=()=>{
return new Promise((resolve,reject)=>{
const err=null;
setTimeout(()=>{ //模拟接口操作请求
if(!err) resolve({code:200,info:{appName:"newAppName"}})
else reject(err)
})
})
}
12. vuex进阶-store插件----持久本地存储:
新建文件:store/plugin/saveInLocal.js封装:定义一个持久化存在在本地的state状态管理的插件,即使每次刷新,也不会影响修改后的值 。
export default state=>{
console.log('store初始化时执行该函数');
if(localStorage.state){store.replaceState(JSON.stringify(localStorage.state))} //如此本地已经存储了,每次刷新页面(即初始化store时),
//就用mutation提交后的本地存储替换提交前的
store.subscribe((mutation,state)=>{
console.log("每次有提交mutation时就执行"); //每次提交mutation时,时都将state值存储在本地
localStorage.state=JSON.stringify(state); //state 是一个对象,转换为json字符串,存储在本地
})
}
在store/index.js中引入:
import saveInLocal from './plugin/saveInLocal.js'
export default new Vuex.Store({
plugins:[saveInLocal] //加载插件
})
13. store下的严格模式:在严格模式下,不能直接修改state里面的值(this.$store.state.user.userName="newName"),需要在mutation提交中修改,否则会报错(虽然也会修改)
在store/index.js下:
export default new Vuex.Store({
strict:true, //是否开启严格模式,process.env.NODE_ENV ==="devalopment" ,在开发环境下是严格模式,否则不是
state , //es6语法相当于state:state
mutations ,
getters,
actions,
mudules:{
user
},
plugins:[saveInLocal] //加载插件
})
14. v-model:如果绑定的属性不是组件data(){return{} }里面的属性,也是state里面的属性,就存在问题,因为state里面的属性时需要在mutation中修改。
v-model:的本质就是:在组件上绑定一个属性传值,再绑定一个@input事件监听,当输入的值有变化时,就直接替换掉原来的值,
解决双休绑定state值的方法:
方法1:
...mapMutations({"SET_STATE_VALUE"})
changeStateVal(val){
this.SET_STATE_VALUE(val) //在mutations.js中设置:SET_STATE_VALUE(state,val{state.stateVal=val} ,在state.js中设置stateVal:"abc"
}
方法2:
...mapMutations({"SET_STATE_VALUE"})
computed:{
stateVal:{ //计算属性里stateVal是对象不是方法,有set()和get()方法
get(){
return this.$store.state.stateVal;
},
set(val){
this.SET_STATE_VALUE(val)
}
}
}
15. ajax请求:
15.1解决跨域问题:
方法1:在vue.config.js里面设置跨域代理请求:
module.exports = {
lintOnSave:false, //是否校验eslint
productionSourceMap:false, //打包时是否生成.map文件
outputDir: 'www', //打包输出的文件名
publicPath: './', //打包输出的位置
devServer: { //跨域代理
open: true,
host: '0.0.0.0',
port: 8082,
https: false,
hotOnly: false,
proxy: { //可设置多个跨域代理
'/api': {//以/api开始的路由的baseUrl由target:'http://xxx.xxx.x.xxx:8083'代理
target: 'http://xxx.xxx.x.xxx:8083',
changeOrigin: true,
ws: false,
secure: false,
pathRewrite:{
'^/api':''
}
},
'/ace': {//以/ace开始的路由的baseUrl由target:'http://xxx.xxx.x.xxx:123'代理
target: 'http://xxx.xxx.x.xxx:123',
changeOrigin: true,
ws: false,
secure: false,
pathRewrite:{
'^/ace':''
}
}
}
}
}
在发送axios请求里执行:
axios.post("/login",{
uname:"Ace",
upwd:"1234"
}).then(data=>{
//返回数据的处理
})
方法二:在后台设置跨越。
在后台(如node的app.js)设置跨域:
app.all("*",(req,res,next)=>{ //*代表所有请求。
req.header("Access-Contral-Allow-Origin","*");
req.header("Access-Contral-Allow-Headers","X-Requested-Width,Content-Type");
req.header("Access-Contral-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");
next();
})
在发送axios请求里执行:
axios.post("http://localhost:3000/login",{
uname:"Ace",
upwd:"1234"
}).then(data=>{
})
15.2 封装axios:
15.2.1 请求/响应拦截:创建文件lib/axios.js:
/*
* 1. 引入axios,创建一个类,封装新的axios。
* 2. "@/config/index.js" export const baseURL=process.env.NODE_ENV==='procution'?'http://127.207.1.460':'' ;
* */
import axios from "axios"
import iview from "iview"
class HttpRequest{
constructor(baseUrl){ //创建constructor方法,设置默认值
this.baseUrl=baseUrl;
this.queue={}; //队列空对象,用于存放所有未请求的url,当队列为空时,表示所有url请求完,这样可以只需要一次添加loading加载效果,不需要重复添加loading效果
}
getInsideConfig(){ //创建一个方法,配置全局通用配置如url,methods,headers等,返回一个内部的配置
const config={
baseUrl:this.baseUrl,
headers:{
'platform':'pc',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
}
}
return config
}
distroy (url) {
delete this.queue[url]
if (!Object.keys(this.queue).length) {
iview.Spin.hide()
}
}
interceptors(instance,url){ //拦截器,参数axios的实例 ,url是用于存放queue空队列中
instance.interceptors.request.use(config=>{ //axios请求拦截器,config是请求成功时的处理函数,error是请求出现错误时的处理函数
//添加请求前的控制,如添加loading....加载效果,如iview的Spin标签
if(!Object.keys(this.queue).length){ //如果this.queue队列所有的Key值组成的数组为空的情况下,即
iview.Spin.show() //显示iview中的加载动画效果
}
this.queue[url]=true; //将请求的url放入队列中
return config
},error=>{
return Promise.reject(error) //请求失败时,直接抛出这个错误
})
instance.interceptors.response.use(res=>{ //axios响应拦截器,对返回的结果的处理筛选
delete this.queue[url] //响应成功后就删除该队列对应的url
this.distroy(url)
return res
// const {data,status}=res //对响应结果进行筛选,只需要data和status值
// return {data,status}
},error=>{
iview.Spin.hide()
this.distroy(url)
delete this.queue[url] //响应失败后就删除该队列对应的url
return Promise.reject(error) //响应失败时,直接抛出这个错误
})
}
request(options){ //创建一个请求request方法,options是单独传入请求的配置 如参数
const instance=axios.create(); //创建一个axios实例
options=Object.assign(this.getInsideConfig(),options); //将内部的配置与传入的配置合并成新的options对象
this.interceptors(instance,options.url); //axios实例为参数的拦截器
return instance(options); //返回axios的实例,配置参数是合并后的参数
}
}
export default HttpRequest
15.2.2 在config/index.js配置基础信息:用于存放项目的配置url。
export const baseUrl=process.env.NODE.ENV==="production"
?"http://www.ace.com" //如果是运行环境,就写运行环境的接口
:"" //如果是在开发环境下,且已经设置了本地跨域代理 devServer:{proxy:'http://localhost:4000'},
//这里设置空字符串,如果没有设置本地跨域代理这里就写'http://localhost:4000'
15.2.3 在api/index.js实例化axios
import HttpRequest from "@/lib/axios"
const axios=new HttpRequest() //创建一个HttpRequest实例
export default axios
15.2.4 在api/user.js中定义login.vue组件的登录接口:
import axios from "./index.js"
import iView from 'iview'
export var getUserInfo=({name})=>{
return axios.request({
url:"/login",
methods:"post",
timeout: 30000,
data:{
name:name
}
})
}
export var sendAxiosHttp=(baseUrl,sendMethod,timeOut,params,func)=>{
axios.request({
url:baseUrl,
methods:"post",
timeout: 30000,
params:params
}).then(data=>{
if(data.status==200){
var dataVal=data.data;
if(dataVal.returnCode=="0"){ //成功获取数据
func(data)
}else if(dataVal.returnCode=="-9995"){ //后台判断登录超时,重新登录
var host=window.location.protocol+"//"+window.location.host;
window.location.href = host + "/login"; //xxx/index.html这是发布的时候默认的登录页面
return;
}else{
iView.Message.info(dataVal.returnMessage)
}
}else{
iView.Message.info("登录接口,网络故障!")
}}).catch( (error)=> {
alert(error);
});
}
15.2.5 在login.vue组件里引入api/user.js中定义好的登录接口方法,然后调用:
import {getUserInfo} from "@/api/user.js"
getInfo(){
getUserInfo({uname:"Ace"}).then(res=>{
console.log(res)
})
}
16. 组件的ID命名,插槽的实现,DOM获取,以及父组件调用子组件的方法:
子:
我是子组件
父:
16.1 如何给组件命名id不会冲突其他组件的id名:this._uid在项目中,每个组件都有独一无二的_uid,给里面组件命名时,带上这个独一无二的_uid就会避免与其他组件命名冲突。
computed:{
myId(){
return `child_${this._uid}`
}
}
16.2 怎么获取子组件dom里面的内容(原生js获取和ref获取):
ref获取:this.$refs.child.innerText
原生js获取:document.getElementById(myId).innerText
16.3 父组件怎么调用子组件的方法:
this.$refs.childSpan.getChildInfo()
17. vue中操作dom元素:通过数据操作dom的width/height/top/bottom....
17.1 通过数据操作dom的width/height/top/bottom....
computed:{
divLeft(){
return ``
},
divWidth(){
return `clac(30% - 4px)` //css3的属性clac()计算出百分比和px的结果, 符号前后必须空格
}
}
17.2 鼠标按下移动事件:
downHandle(e){
document.addEventListener("mousemove/mouseup",mousemoveHandle/mouseupHanlde) ; //给doucment绑定鼠标移动、松开事件
e.pageX //是获取触发downHandle的是鼠标距离页面左边的距离
this.$refs.div.getBoundingClientRect() ; //返回ref='div'元素domwidth,height,top,bottom,right....等信息的对象。
}
18. 在组件引入外部css的3种方法:
在script里面:import "../index.css"
在style里面:@import "../index.css"
在style里面:@import url("../index.css")