1、全局属性
vue2:
对于一些第三方插件,vue2中通常使用prototype
原型来挂载到vue对象中
import Vue from 'vue'
Vue.prototype.$http=Axiox
Vue.prototype.$echart= Echart
vue3:
vue3中提供了一个名为globalProperties
的全局属性配置,可以代替vue2
app.config.globalProperties.$http = Axios
app.config.globalProperties.$echart = Echart
2、main.js对比
3、store的使用对比
4、路由router的使用对比
5、Vue3生命周期变化以及setup函数
与 2.x 版本生命周期相对应的组合式 API
1.beforeCreate -> 使用 setup()
2.created -> 使用 setup()
3.beforeMount -> onBeforeMount
4.mounted -> onMounted
5.beforeUpdate -> onBeforeUpdate
6.updated -> onUpdated
7.beforeDestroy -> onBeforeUnmount
8.destroyed -> onUnmounted
9.errorCaptured -> onErrorCaptured
beforeCreate和created会在setup中执行
无法使用this拿到当前组件的相关数据 setup只能同步执行,不可以异步
6、ref,reactive响应式api
被响应式api标记过的数据才可以成为响应式数据
ref–用来标记简单类型数据
reactive—标记复杂类型数据(深度响应式)
如果用ref对象/数组, 会自动将对象/数组转换为reactive的代理对象
ref的数据操作: 在js中要.value, 在模板中不需要(内部解析会自动添加.value)
7、Element-plus引入
使用cli安装 vue add element-plus
官网路径: https://element-plus.gitee.io/#/zh-CN
8、引入http请求框架axios
安装 axios http请求框架 npm install axios
axios基础使用方法:
axios.create({config}) //创建axios实例
axios.get(url,{config}) //get请求
axios.post(url, data,{config}) //post请求
axios.interceptors.request.use() // 请求拦截器
axios.interceptors.response.use() // 请求拦截器
封装后的axios
import axios from 'axios'
//axios.create 创建一个axios实例 我们给这个实例编写配置,后续所有通过实例发送的请求,都受当前配置约束
const $http = axios.create({
baseURL: '',
timeout: 1000,
// headers: {'X-Custom-Header': 'foobar'}
});
// 添加请求拦截器
$http.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
config.headers.token='123123123'
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
// 添加响应拦截器
$http.interceptors.response.use(function (response) {
// 对响应数据做点什么
let data=response.data;
return data;
}, function (error) {
// 对响应错误做点什么
return Promise.reject(error);
});
export default $http
http/api.js
import $http from './index.js'
export const getCourseList= (data)=>{
return $http.get('http://jsonplaceholder.typicode.com/posts',data)
}
export const login=(data)=>{
return $http.post('http://jsonplaceholder.typicode.com/posts',data)
}
login.vue
<template>
<div class="login">
<h4>朝夕后台管理系统</h4>
<el-form
label-width="80px"
:model="loginData"
>
<el-form-item label="账号">
<el-input placeholder="请输入账号" v-model="loginData.name"></el-input>
</el-form-item>
<el-form-item label="密码">
<el-input placeholder="请输入密码" v-model="loginData.password" show-password></el-input>
</el-form-item>
<el-form-item>
<el-button @click="subFun" class="sub-btn" type="primary">登录</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
import {reactive } from 'vue'
import { ElMessage } from 'element-plus'
import {login} from '../http/api.js'
import router from '../router/index.js'
export default {
name: "Login",
setup() {
let loginData=reactive({
name:'',
password:''
})
let subFun =()=>{
//先判断账号密码是否已经填写
if(!loginData.name||!loginData.password){
ElMessage.error('请先填写账号和密码');
return
}
login(loginData).then(res=>{
console.log(res)
router.push('/Home')
})
}
return{
loginData,
subFun
}
},
};
</script>
<style scoped>
.login{
width: 500px;
margin: 150px auto;
border:1px solid #efefef;
border-radius: 10px;
padding:20px
}
h4{
text-align: center;
}
.sub-btn{
width: 100%;
}
</style>
9、axios跨域处理
①项目根目录创建vue.config.js
②写入devServe配置
③解析devServe配置
10、Vue路由
路由模式
1、 Hash模式 通过 URL 的 hash 来模拟一个完整的 URL于是当 URL 改变时,页面不会重新加载
2、History模式 利用了 HTML5 History Interface 中新增的 pushState() 和 replaceState() 修改浏览器的历史记录栈
动态路由
路由传参
1.通过 标签
<router-link :to="{name:xxx,params:{key:value}}">valueString</router-link> //$route.params.key
2.url传参
path:'/params/:newsId/:newsTitle', //$route.params.newsId
3.query传参
{ name:'xxx',query: { queryId: status }} //$route.query.queryId
路由跳转方式
1、<ruter-link/>
1. 不带参数
<router-link :to="{name:'home'}">
<router-link :to="{path:'/home'}"> //name,path都行, 建议用name
// 注意:router-link中链接如果是'/'开始就是从根路由开始,如果开始不带'/',则从当前路由开始。
**router-link 不带参数 path中不加/表示相对路径,还得加一个append属性才能跳转到相对路径**
2.带参数
<router-link :to="{name:'home', params: {id:1}}">
// params传参数 (类似post)
// 路由配置 path: "/home/:id" 或者 path: "/home:id"
// 不配置path ,第一次可请求,刷新页面id会消失
// 配置path,刷新页面id会保留
// html 取参 $route.params.id
// script 取参 this.$route.params.id
<router-link :to="{name:'home', query: {id:1}}">
// query传参数 (类似get,url后面会显示参数)
// 路由可不配置
// html 取参 $route.query.id
// script 取参 this.$route.query.id
2、this.$router.push()
(函数里面调用)
1. 不带参数
this.$router.push('/home')
this.$router.push({name:'home'})
this.$router.push({path:'/home'})
2. query传参
this.$router.push({name:'home',query: {id:'1'}})
this.$router.push({path:'/home',query: {id:'1'}})
// html 取参 $route.query.id
// script 取参 this.$route.query.id
3. params传参
this.$router.push({name:'home',params: {id:'1'}}) // 只能用 name
// 路由配置 path: "/home/:id" 或者 path: "/home:id" ,
// 不配置path ,第一次可请求,刷新页面id会消失
// 配置path,刷新页面id会保留
// html 取参 $route.params.id
// script 取参 this.$route.params.id
4. query和params区别
query类似 get, 跳转之后页面 url后面会拼接参数,类似?id=1, 非重要性的可以这样传, 密码之类还是用params刷新页面id还在
params类似 post, 跳转之后页面 url后面不会拼接参数 , 但是刷新页面id 会消失
3、 this.$router.replace()
(用法同上,push)
4、 this.$router.go(n)
this.$router.go(n)
向前或者向后跳转n个页面,n可为正整数或负整数
ps : 区别
this.$router.push
跳转到指定url路径,并想history栈中添加一个记录,点击后退会返回到上一个页面
this.$router.replace
跳转到指定url路径,但是history栈中不会有记录,点击返回会跳转到上上个页面 (就是直接替换了当前页面)
this.$router.go(n)
向前或者向后跳转n个页面,n可为正整数或负整数
十一、动态路由(角色权限为例子)
1、通过动态添加路由和菜单显示来控制,不能访问的页面不添加到路由表和菜单表里面。
2、所有页面都展示,但是访问的时候做下判断限制,如果是没有权限的角色访问,跳转到404页面。
思路:
在每一个路由的 meta
属性里,将能访问该路由的角色添加到 roles
里。用户每次登陆后,将用户的角色返回。然后在访问页面时,把路由的 meta
属性和用户的角色进行对比,如果用户的角色在路由的 roles
里,那就是能访问,如果不在就拒绝访问。
路由信息
routes: [
{
path: '/login',
name: 'login',
meta: {
roles: ['admin', 'user']
},
component: () => import('../components/Login.vue')
},
{
path: 'home',
name: 'home',
meta: {
roles: ['admin']
},
component: () => import('../views/Home.vue')
},
]
页面控制
// 假设角色有两种:admin 和 user
// 这里是从后台获取的用户角色
const role = 'user'
// 在进入一个页面前会触发 router.beforeEach 事件
router.beforeEach((to, from, next) => {
if (to.meta.roles.includes(role)) {
next()
} else {
next({path: '/404'})
}
})
通过token验证登录语法
router.beforeEach((to, from, next) => {
// 如果有token 说明该用户已登陆
if (localStorage.getItem('token')) {
// 在已登陆的情况下访问登陆页会重定向到首页
if (to.path === '/login') {
next({path: '/'})
} else {
next({path: to.path || '/'})
}
} else {
// 没有登陆则访问任何页面都重定向到登陆页
if (to.path === '/login') {
next()
} else {
next(`/login?redirect=${to.path}`)
}
}
})
动态生成路由
利用 vue-router 的 addRoutes 方法可以动态添加路由
router.addRoutes(routes: Array<RouteConfig>)
动态添加更多的路由规则。参数必须是一个符合 routes
选项要求的数组。
routes数组示例:
const router = new Router({
routes: [
{
path: '/login',
name: 'login',
component: () => import('../components/Login.vue')
},
{path: '/', redirect: '/home'},
]
})
上面的代码和下面的代码效果是一样的
const router = new Router({
routes: [
{path: '/', redirect: '/home'},
]
})
router.addRoutes([
{
path: '/login',
name: 'login',
component: () => import('../components/Login.vue')
}
])
在动态添加路由的过程中,如果有 404 页面,一定要放在最后添加,否则在登陆的时候添加完页面会重定向到 404 页面。
类似于这样,这种规则一定要最后添加。
{path: '*', redirect: '/404'}
假设后台数据menu
// 左侧菜单栏数据
menuItems: [
{
name: 'home', // 要跳转的路由名称 不是路径
size: 18, // icon大小
type: 'md-home', // icon类型
text: '主页' // 文本内容
},
{
text: '二级菜单',
type: 'ios-paper',
children: [
{
type: 'ios-grid',
name: 't1',
text: '表格'
},
{
text: '三级菜单',
type: 'ios-paper',
children: [
{
type: 'ios-notifications-outline',
name: 'msg',
text: '查看消息'
},
{
type: 'md-lock',
name: 'password',
text: '修改密码'
},
{
type: 'md-person',
name: 'userinfo',
text: '基本资料',
}
]
}
]
}
]
示例菜单栏,可以参考iview组件或者elementUI-plus版本的组件
<!-- 菜单栏 -->
<Menu ref="asideMenu" theme="dark" width="100%" @on-select="gotoPage"
accordion :open-names="openMenus" :active-name="currentPage" @on-open-change="menuChange">
<!-- 动态菜单 -->
<div v-for="(item, index) in menuItems" :key="index">
<Submenu v-if="item.children" :name="index">
<template slot="title">
<Icon :size="item.size" :type="item.type"/>
<span v-show="isShowAsideTitle">{{item.text}}</span>
</template>
<div v-for="(subItem, i) in item.children" :key="index + i">
<Submenu v-if="subItem.children" :name="index + '-' + i">
<template slot="title">
<Icon :size="subItem.size" :type="subItem.type"/>
<span v-show="isShowAsideTitle">{{subItem.text}}</span>
</template>
<MenuItem class="menu-level-3" v-for="(threeItem, k) in subItem.children" :name="threeItem.name" :key="index + i + k">
<Icon :size="threeItem.size" :type="threeItem.type"/>
<span v-show="isShowAsideTitle">{{threeItem.text}}</span>
</MenuItem>
</Submenu>
<MenuItem v-else v-show="isShowAsideTitle" :name="subItem.name">
<Icon :size="subItem.size" :type="subItem.type"/>
<span v-show="isShowAsideTitle">{{subItem.text}}</span>
</MenuItem>
</div>
</Submenu>
<MenuItem v-else :name="item.name">
<Icon :size="item.size" :type="item.type" />
<span v-show="isShowAsideTitle">{{item.text}}</span>
</MenuItem>
</div>
</Menu>
其实就是通过三次 v-for 不停的对子数组进行循环,生成三级菜单。
基础动态路由
const asyncRoutes = {
'home': {
path: 'home',
name: 'home',
component: () => import('../views/Home.vue')
},
't1': {
path: 't1',
name: 't1',
component: () => import('../views/T1.vue')
},
'password': {
path: 'password',
name: 'password',
component: () => import('../views/Password.vue')
},
'msg': {
path: 'msg',
name: 'msg',
component: () => import('../views/Msg.vue')
},
'userinfo': {
path: 'userinfo',
name: 'userinfo',
component: () => import('../views/UserInfo.vue')
}
}
// 传入后台数据 生成路由表
menusToRoutes(menusData)
// 将菜单信息转成对应的路由信息 动态添加
function menusToRoutes(data) {
const result = []
const children = []
result.push({
path: '/',
component: () => import('../components/Index.vue'),
children,
})
data.forEach(item => {
generateRoutes(children, item)
})
children.push({
path: 'error',
name: 'error',
component: () => import('../components/Error.vue')
})
// 最后添加404页面 否则会在登陆成功后跳到404页面
result.push(
{path: '*', redirect: '/error'},
)
return result
}
function generateRoutes(children, item) {
if (item.name) {
children.push(asyncRoutes[item.name])
} else if (item.children) {
item.children.forEach(e => {
generateRoutes(children, e)
})
}
}
动态路由的其他配置信息链接
十二、vuex全局状态
Vuex 数据操作流程
1.state 应用层级的状态应该集中到单个 store 对象中
-
mutation 更改状态的唯一方法,并且这个过程是同步的
-
action 异步逻辑都应该封装到 action 里面
属性存储
vue2
export default {
props: {
title: String
},
data () {
return {
username: '',
password: ''
}
}
}
vue3
新的setup()
方法,此方法在组件初始化构造的时候触发