VUE项目

105 阅读9分钟

VueCLI

VuCLI是针对vue项目的一个脚手架工具。

脚手架工具安装

在全局安装VueCLI脚手架工具
npm install -g@vue/cli

通过VueCLI创建项目

脚手架工具安装完成后,通过以下命令创建Vue项目
vue create 项目根目录名称
or
vue ui

安装模式

提示选择安装方式:
vue2默认安装方式
vue3默认安装方式
自定义安装方式

安装插件

根据实际项目需求选择要安装的插件
babel
router
vuex

选择配置文件

单独生成一个配置文件
在package.json中进行配置

是否保存当前设置

如果要保存当前选择的这些配置,输入y,否则输入n。
如果选择保存,会提示输入一个名称。

开始创建项目

启动项目

项目创建完成后,在命令行中进入项目根目录,然后通过以下命令来启动项目
npm run serve

vue中使用sass

<style lang="scss">
//npm i sass-loader sass
.one{
    h1{
        color:red;
    }
}
</style>

在vue中使用axios

axios
axios是vue中用来代替ajax的另一种发送网络请求的方式。
下载
任何项目中,要使用axios,都需要先安装并在项目中引用。
1.安装
npm i axios

2.项目中引入

import axios from 'axios';

3.使用

//msg就是axios请求得到的结果,其中包含了后端返回的数据data
const msg = await axios({
    url:'/students/getStudents', //前后端连接的接口
    methods:'get', //请求类型:get、post、delete、put
    //传参:前端传递给后端的数据
    params:{
        currentPage:
    }
})

const msg = await axios({
    url:'/students/addStudents', //前后端连接的接口
    methods:'post',//请求类型:get、post、delete、put
    //传参:前端传递给后端的数据
    data:{
        name:'star night'
    }
})

Vue中访问服务器

默认情况下,通过VueCLI脚手架工具搭建的vue项目,都有自己的服务器。默认端口8080.但是,在实际项目中,我们需要从vue中去请求其他的服务器(例如:nodejs的服务器),因此,需要在vue中对要访问目标服务器地址进行配置。
在Vue项目根目录创建一个vue.config.js的配置文件,然后写入以下代码:
//vue.config.js
module.exports = {
    devServer:{
        proxy:'http://localhost:3000' //'http://localhost:3000'是目标服务器地址
    }
}

SPA

SPA:single page application,单页应用,就是指整个应用程序只有一个html页面。

Vue Router路由

路由的初始化

服务端路由指的是服务器根据用户访问的 URL 路径返回不同的响应结果。当我们在一个传统的服务端渲染的 web 应用中点击一个链接时,浏览器会从服务端获得全新的 HTML,然后重新加载整个页面。
然而,在单页面应用中,客户端的 JavaScript 可以拦截页面的跳转请求,动态获取新的数据,然后在无需重新加载的情况下更新当前页面。这样通常可以带来更顺滑的用户体验,尤其是在更偏向“应用”的场景下,因为这类场景下用户通常会在很长的一段时间中做出多次交互。

路由的跳转

router-link组件跳转
<router-link to="/register">注册</router-link>
<router-link :to="{path:'/register'}">注册</router-link>
<router-link :to="{name:'Register'}">注册</router-link>

点击事件跳转

export default{
    methods:{
        register(){
            //跳转到登录界面
                //this.$router.path("/login");
                
                //this.$router.push({
                    //path:"/login",
                    //});
                    
                  //this.$router.push({
                    //  name:"Login",
                 // });
                //replace跳转后,新页面的访问记录会替换掉旧页面的访问记录
                this.$router.replace("/login");
                //跳转至指定的历史记录
                this.$router.go(1);
            },
        },
    };

动态路由

动态路由指的是在做路由跳转时,路由中有一部分内容是动态变化的。
例如/system/star,/system/night,其中star和night就是路由中动态的那一部分。
动态路由的配置
{
    path:'/system/:name',
    name:'System',
        component:System
},

其中/:name中的name,是任意定义的一个变量名,就用来匹配路由中动态的那一部分内容。
获取动态路由的数据
1.通过this.$route.params:

this.$route.params.name;

在组件中,可以通过this.$route.params获取到动态路由的参数对象。该对象中就包含了我们在路由中传递的数据。
2.通过props获取
在配置动态路由时,添加一个props:true的属性:

{
    path:'/system/:name',
    name:'System',
    props:true,
    component:System
},

在组件中,就可以直接通过props来接收路由中的参数:

export default{
    props:["name"],
};

嵌套路由

嵌套路由是指,可以在一个路由中嵌套其他的子路由。
嵌套路由的配置
Vue Router中,给每一个路由都提供了一个children的属性,用来配置子路由(嵌套路由)。
{
    path:'/system',
    name:'System',
    props:true,
    component:System,
    children:[
    {
        path:'studentsList',
        component:() => import('../components/students/StudentsList.vue')
    },
    {
        path:'addStudents',
        component:() => import('../components/students/AddStudents.vue')
    }
  ]
},

配置成功后,浏览器就可以通过/system/studentsList和/system/addStudents路径去访问到System组件中的StudentsList和AddStudents组件。

路由的重定向

路由重定向指的是:用户在访问地址 A 的时候,强制用户跳转到地址 C ,从而展示特定的组件页面;通过路由规则的 redirect 属性,指定一个新的路由地址,可以很方便地设置路由的重定向:
var router = new VueRouter({ 
    routes: [      
        // 其中,path 表示需要被重定向的原地址,redirect 表示将要被重定向到的新地址  
        //当用户在地址栏中输入`/`,会自动的跳转到`/user`,而`/user`对应的组件为User
       {path:'/', redirect: '/user'}, 
      {path:'/user',component: User}, 
      {path:'/register',component: Register} 
    ] 
  })

导航守卫

当路由正在发生改变时,例如从/login跳转到/register,在进入注册页面前,我们可以添加一些导航守卫来决定是否进入该页面。
分类:
1.全局前置守卫
const router = new VueRouter({
    routes,
    mode:'history'
})
//全局前置守卫
let isLogin = false;
router.beforeEach((to,from,next) => {
//to表示想要进入的路由对象,包含该路由对象的相关信息
//from表示即将离开的路由对象,包含该路由对象的相关信息
//next方法,通过调用该方法来决定路由的去向
//next():直接通过
//next('/login')跳转到login路由

    if(to.path.includes('/system')){
        if(isLogin){
            next();
        } else {
            if(from.path.includes('/login')){
            } else {
                   next('/login');
               }
        }
    } else {
        next();
    }
})

2.路由独享守卫

const router = new VueRouter({
    routes:[
        {
            path:'/foo',
            component:Foo,
            beforeEnter:(to,from,next) => {  //方法参数和全局前置守卫一致
                //....
            }
        }
    ]
})

3.组件内的守卫

const Foo={
    template:'...',
    beforeRouteEnter(to,from,next){
        //在渲染该组件的对应路由被confirm前调用
        //不能获取组件实例‘this’
        //因为当守卫执行前,组件实例还没被创建
    },
    beforeR欧特Update(to,from,next){
        //在当前路由改变,但是该组件被复用时调用
        //例子:对于一个带有动态参数的路径/foo/:id,在/foo/1和/foo/2之间跳转的时候,由于会渲染同样的foo组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用
        //可以访问组件实例'this'
    },
    beforeRouteLeave(to,from,next){
        //导航离开该组件的对应路由时调用
        //可以访问组件实例'this'
    }
}

路由元信息

在路由配置文件中,每一个路由对象都有一个meta属性,可以用来设置任意属性值。
例如,我们在对路由组件实现keep-alive加载时,可以通过meta属性添加一个keepAlive 的状态值来对组件进行判断。
{
    path:'studentsList',
    component:()=>import('../components/students/StudentsList.vue'),
    meta:{
    keepAlive:false
    }
},
{
    path:'addStudents',
    component:()=>import('../components/students/AddStudents.vue'),
    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>

Vuex

Vuex是一个专为Vue.js应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
export default new Vuex.Store({
    //等同于组件中的data
    state:{
        count:0
    },
    getters:{
    },
    //等同于组件中的methods,用于定义修改state的方法
    //注意:修改state的唯一路径,就是通过mutations中的方法去进行修改
    mutations:{
        //mutations中定义的所有方法,第一参数接收的都是state对象
        increment(state){
            state.count++;
        },
        decrement(state){
            state.count--;
        },
        inputCount(state,newCount){
            state.count = newCount;
        },
    },
    acitons:{
    },
    modules:{
    }
})

辅助函数

我们常规的从vuex中获取state的方式是$store.state,调用vuex中的mutations方法语法是$store.commit()。在实际项目中,当我们要获取的数据或调用的方法过多时,常规的方法使用起来就不那么方便了,所以,vuex中还提供了辅助函数帮助我们快速的去获取和调用仓库中的数据和方法。
获取辅助函数
import {mapState,mapMutations,mapGetters,mapActions} from 'vuex';

mapState 用于获取state的数据
mapGetters 用于获取getters的数据
mapMutation 用于获取matations的方法
mapActions 用于获取actions的方法
其中,mapState和mapGetters,在组件的computed中调用

computed:{
    ...mapState(['name','age']),
    ...mapGetters(['name','age']),
}

mapMutations和mapActions,在组件的methods中调用

methods:{
    ...mapMutations(['add','deleteOne']),
    ...mapActions(['getStudents','add']),
}

actions
用于定义异步方法
actions中的所有方法,第一个参数接收的都是context对象,实际指的是store对象。如果想要通过actions的方法去修改state,只能在actions中先调用mutations的方法,然后通过mutations的方法去修改state。

actions:{
    //获取学生数据
    async getStudents(context){
        const {data} = await axios({
            url:"/students",
            method:"get",
            params:{
                currentPage:1,
                pageSize:10,
            },
        });
        if(data.status){
            //action调用mutation通过context.commit();
            context.commit('changeStudentsData',data.data);
            //action调用action的方法通过context.dispatch();
            }
        },
    },
    modules:{
    }

vuex模块化

当我们开发的项目比较大时,store中的数据就可能比较多,这时我们store中的数据就可能变得臃肿,为了解决这一问题,我们就需要将store模块化(module),即每个模块拥有自己的state、mutation、action、getter、甚至是嵌套子模块。
模块a:a.js
export default {
    namespaced: true,//开启命名空间
    state: {
    	sum:0,
    	number:0
    },
    mutations: {
    	ADD_NUM(state,value){
  			state.sum+=value
  		}
    },
    actions: {
    },
}

模块b:b.js

export default {
    namespaced: true,//开启命名空间
    state: {
    	name:'张三',
    	personList:[]
    },
    mutations: {
    	ADD_PERSON(state,value){
    		state.personList.unShift(value)//把数据添加到数组的首位
    	}
    },
    actions: {
    	addZhang(context,value){
    		//value参数是一个对象
    		if(value.name.indexOf('张')===0){
    			context.commit('ADD_PERSON')
    		}else{
    			alert('这个人不姓张!')
    		}
    	},
    	addServer(context,value){
    		axios.get('https://api.uixsj.cn/hitokoto/get?type=social').then(res=>{
    			context.commit('ADD_PERSON',{name:res.data})
    		},error=>{
    			alert(error.message)
    		})
    	}
    },
    getters: {
    	firstName(state,getters){
 			return state.personList[0].name+',你好!'
 		}
    }
}

store/index.js:在modules中引入

import Vue from 'vue'
import Vuex from 'vuex'

//引入
import moduleA from './a.js'
import moduleB from './b.js'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
  },
  mutations: {
  },
  actions: {
  },
  modules: {//模块化
    moduleA,
    moduleB
  }
})

在项目中进行调用

访问state数据:
第一种方式:this.$store.state.moduleA.sum
第二种方式:
 import {mapState} from 'vuex'
 computed:{
 	...mapState('moduleA',['sum','number'])
 }
修改state数据:
第一种方式:this.$store.commit('moduleA/ADD_NUM',10)
第二种方式:
 import {mapMutations} from 'vuex'
 methods:{
	...mapMutaions('moduleA',['ADD_NUM']),
 	}
        
action提交mutation:
第一种方式:this.$store.dispatch('moduleB/addZhang',{name:'小明',age:18}) this.$store.dispatch('moduleB/addServer')
第二种方式:
 <button @click="addZhang({name:'张2'})">添加姓张的人+</button>

 import {mapActions} from 'vuex'
 methods:{
 	...mapActions('moduleB',['addZhang']),//参数在调用时传递 addZhang({name:'小明',age:18})
 }
getters ,store的计算属性(加工数据)
第一种方式:this.$store.getters['moduleB/firstName']
第二种方式:
 import {mapGetters} from 'vuex'
 computed:{
 	...mapGetters('moduleB',['firstName'])//数组写法(简写)
 }

axios实例封装

// 对axios进行封装处理
import axios from 'axios'

// 1.在axios的基础之上创建了一个新的axios实例:instance
const instance = axios.create({
  baseURL: 'http://localhost:3000',
  timeout: 5000
})
// 2.配置axios请求拦截器
instance.interceptors.request.use((config) => {
  // 拦截成功
  // 2.1获取token
  const token = localStorage.token
  // 2.2将token添加到请求头的Authorization属性中
  config.headers.Authorization = 'Bearer' + token
  return config
}, (err) => {
  // 拦截失败
  return err
})

// 3.配置axios响应拦截器
instance.interceptors.response.use((res) => {
  // 响应成功
  return res // {data:[]}
}, (err) => {
  // 响应失败
  // 判断错误类型,并将错误的描述添加到err.message属性上
  if (err && err.response) {
    switch (err.response.status) {
      case 400:err.message = '请求错误(400)'; break
      case 401:err.message = '身份认证失败(401)'; break
      case 403:err.message = '拒绝访问(403)'; break
      case 404:err.message = '请求出错(404)'; break
      case 408:err.message = '请求超时(408)'; break
      case 500:err.message = '服务器错误(500)'; break
      case 501:err.message = '服务未实现(501)'; break
      case 502:err.message = '网络错误(502)'; break
      case 503:err.message = '服务不可用(503)'; break
      case 504:err.message = '网络超时(504)'; break
      case 505:err.message = 'HTTP版本不受支持(505)'; break
      default:err.message = `连接出错(${err.response.status})`
    }
    return {
      data: {
        status: 0,
        type: err.response.status,
        message: err.message
      }
    }
  } else {
    err.message = '服务器连接失败'
    return {
      data: {
        status: 0,
        message: err.message
      }
    }
  }
})

// 将创建和配置完成的新的axios实例暴露出去
export default instance

请求api的封装

//引入我们自己封装过的axios
import axios from '../../axios.js';
const students = {
    getStudents:function (params){
        return axios({
           url:'/students',
           method:'get',
           params
       });
   },
   addStudents:function(data){
       return axios({
           url:'/students/addStudents',
           method:'post',
           data
       })
   },
   deleteStudents(data) {
       return axios({
           url:'/students',
           method:'delete',
           data
       })
   }
}
export default students;

//apis.js
import students from './modules/students.js';

const apis = {
    students
}

export default apis;

ElementUI

安装

npm安装

使用npm的方式安装,能更好的和webpack打包工具配合使用。
npm i element-ui -s

CDN

目前可以通过 unpkg.com/element-ui 获取到最新版本的资源,在页面上引入js和css文件即可开始使用。
引入样式
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
引入组件库
<script src="https://unpkg.com/element-ui/lib/index.js"></script>