第一节、vue全家桶概念
- 基础功能 + 2个额外功能(vuex、router)
- vuex:仓库,用来存储数据,比如userInfo
- router:路由:组件跳转(类似于动态组件)
第二节、新建vue全家桶项目
1 vue create v2_family
2 Manually select features --自己选择引入哪些插件
3
? Check the features needed for your project: (Press <space> to select, <a> to toggle all, <i> to invert selection, and <enter> to
proceed)
(*) Babel
( ) TypeScript
( ) Progressive Web App (PWA) Support
(*) Router
(*) Vuex
>(*) CSS Pre-processors --scss
( ) Linter / Formatter
( ) Unit Testing
( ) E2E Testing
4
? Choose a version of Vue.js that you want to start the project with
3.x
> 2.x
5
Use history mode for router? (Requires proper server setup for index fallback in production) (Y/n) n
6
? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default): (Use arrow keys)
> Sass/SCSS (with dart-sass)
Less
Stylus
7
? Where do you prefer placing config for Babel, ESLint, etc.? (Use arrow keys)
> In dedicated config files -- Babel, ESLint, etc.单独配置
In package.json -- Babel, ESLint, etc.合并到package.json
8
Save this as a preset for future projects? (y/N) n 不保存这次选择
项目结构
src
assets:图片
components:组件
router-路由
store-仓库
views -组件
App.vue -入口组件
main.js
components和views的区别
views:装一次性组件(不会复用),比如login.vue、regsiter.vue、stuList.vue等
components:装的经常被复用的组件
第三节、路由 (vue router)
后端路由 :请求地址 和 后端js文件中函数 的映射
前端路由:请求地址 和 前端组件的映射
单页面应用 spa(single page application)
-
vue项目都是单页面应用(public/index.html),整个项目是由一个一个的组件来组成,需要搭配路由实现看起来像多页面应用(组件的切换)
-
特点:
只会在第一次访问时才获取资源,以后的跳转就不会再从服务器拿取资源(跳转的还是当前页面,只是不同的组件而已) -
优点:
只要加载完后,则今后的跳转速度是很快 -
缺点:
1 第一次加载很慢,白屏时间比较大(加载所有资源)
2 不利于SEO搜索引擎的优化
seo优化执行原理:需要爬虫来爬页面节点,爬虫需要源码中有节点 ,nuxt.js能够让vue利于SEO
多页面是利于SEO优化
vue框架场景
- vue后台管理系统
路由在项目中的结构
src/router/index.js
//引入Vue
import Vue from 'vue'
//引入路由
import VueRouter from 'vue-router'
//使用路由
Vue.use(VueRouter)
//定义数组---配置
const routes = [
]
//实例化全局路由对象
const router = new VueRouter({
//路由模式
mode: 'history',
//把routes数组配置进来
routes
})
//把路由对象暴露出来
export default router
main.js
import Vue from 'vue'
import App from './App.vue'
//引入路由对象
import router from './router'
import store from './store'
Vue.config.productionTip = false
new Vue({
//注入到Vue实例,意味着我们会今后可以在任意组件中通过this.$xx来获取路由对象
router,
store,
render: h => h(App)
}).$mount('#app')
路由模式
分为hash模式和history模式,有区别
hash模式:http://localhost:8080/#/路径...
缺点:丑(多了#)
优点:一般不会出问题,#就是在提醒浏览器访问的是前端的项目
history模式:http://localhost:8080/路径...
优点:符合地球人的审美
缺点:
1 打包后可能会导致路径404错误(通过配置解决)
2 打包后可能会因为F5刷新导致程序去寻找后台接口(能够通过配置nginx解决等)
配置:
const router = new VueRouter({
//路由模式,如果不写mode属性,则默认也是hash
mode: 'history/hash',
})
路由基本配置:
组件分为两种类型:
1 非路由版 import 引入+components注册+当成标签使用
2 路由版: 组件渲染与否跟着路径走
步骤1: 配置路由文件router/index.js:
//引入Vue
import Vue from 'vue'
//引入路由
import VueRouter from 'vue-router'
//使用路由
Vue.use(VueRouter)
import Login from '@/views/Login.vue'
import Register from '@/views/Register.vue'
//定义数组---配置
const routes = [
// 路由和组件的映射都是以对象代表
{
//路径以/开头
path:"/login",
component:Login
},
{
path:"/register",
component:Register
}
]
//实例化全局路由对象
const router = new VueRouter({
//路由模式
// mode: 'history',
//把routes数组配置进来
routes
})
//把路由对象暴露出来
export default router
步骤2 :在组件中配置路由的出口
<router-view></router-view>
步骤3:访问:
http://localhost:8080/#/login
http://localhost:8080/#/register
练习:通过路由配置登录和注册组件,分别试试hash、history模式
路由的跳转
标签版:
<router-link to="/register">跳转到注册</router-link>
js版
this.$router.push("/login")
路由的其他配置
1 重定向
当访问path路径时,能够自动跳转到redirect路径
//定义数组---配置
const routes = [
// 路由和组件的映射都是以对象代表
//重定向
{
path:"/",
redirect:"/login"
},
2 路由懒加载 (性能)
概念:指的是可以让指定的组件在首次访问时不会被加载,只有当真正路径跳转到对应组件时才会加载
作用:减少了首屏加载时间
使用场景:某些访问频率非常低的组件
做法:
{
path:"/register",
component:()=>import('@/views/Register.vue')
},
或者
let Register=()=>import('@/views/Register.vue')
{
path:"/register",
component:Register
}
3 路由别名
可以为组件多添加一个路径时
{
//路径以/开头
path:"/login",
component:Login,
alias:"/login2"
}
4 通用路由
如果访问不匹配的路径,则页面无法渲染,如何解决?
访问错误的路径,我们也给他一个对应组件
let My404 = () => import('@/views/404.vue')
{
path: "/*",
component: My404
},
嵌套路由(子路由)
如果要实现在某个路由对应的组件中进行局部组件切换,那么就需要在该路由配置中添加子路由(children),同理也需要在路由对应的组件中去添加新的路由出口( )
路由配置文件:
{
path: "/main",
component: Main,
children:[
{
path: "stuList",
component: StuList
},
{
path: "/main/stuAdd",
component: StuAdd
},
]
},
main组件:
<template>
<div>
<h1>学生管理系统</h1>
<div style="display:flex">
<div>
<ul>
<li> <router-link to="/main/stuList">学生列表</router-link> </li>
<li> <router-link to="/main/stuAdd">学生添加</router-link> </li>
</ul>
</div>
<div>
<router-view></router-view>
</div>
</div>
</div>
</template>
动态路由
指的就是路由传参
方式1配置版(路由文件)
1 配置路由文件
{
//语法:/路径/:自定义参数/.....
//?代表该参数可传可不传
path: "/register/:id?/:name?",
component: Register
},
2 传递参数
<template>
<div>
<h1>登录</h1>
<router-link to="/register/1/张三">跳转到注册</router-link>
<router-link :to="`/register/${id}/${name}`">跳转到注册</router-link>
<button @click="go">跳转到注册2</button>
</div>
</template>
<script>
export default {
data(){
return{
id:11,
name:"王五"
}
},
methods:{
go(){
this.$router.push("/register/2/李四")
}
}
}
</script>
<style>
</style>
3 接受参数
3.1 通过$route接受
created(){
console.log(this.$route.params);
}
3.2 通过props
配置路由:
{
//语法:/路径/:自定义参数/.....
//?代表该参数可传可不传
path: "/register/:id?/:name?",
props:true,
component: Register
},
接受:
export default {
props:['id','name'],
方式2:非配置版
1 路由传参:
<template>
<div>
<router-link to="/register?id=1&&name=张三">跳转到注册</router-link>
<router-link :to="`/register?id=${id}&&name=${name}`">跳转到注册</router-link>
<router-link :to="{path:'/register',query:{id:1,name:'小明'}}">转到注册</router-link>
<button @click="go">跳转到注册2</button>
</div>
</template>
<script>
export default {
data(){
return{
id:11,
name:"王五"
}
},
methods:{
go(){
// this.$router.push("/register?id=1&&name=张三")
this.$router.push({
path:"/register",
query:{
id:this.id,
name:this.name
}
})
}
}
}
</script>
<style>
</style>
2 接受参数
this.$route.query
其他:通过name属性来跳转
配置文件:需要配置name属性
{
//语法:/路径/:自定义参数/.....
//?代表该参数可传可不传
path: "/register",
name:"reg",
component: Register
},
跳转:
this.$router.push({
// path:"/register",
name:'reg',
query:{
id:this.id,
name:this.name
}
})
接受:
this.$route.query
路由的元信息
我们可以为每一个路由(路由文件中数组中的每一个元素)配置标记meta(数据),今后可以在该路由对应的组件中获取到标记,从而实现某些业务
换句话说,所谓的元信息就是为路由绑定属性,以后可以在对应组件中获取属性
语法:
存储
{
path: "/login",
component: Login,
meta:{
name:"张三",
obj:{,,,},
arr:[...]
}
},
获取:
this.$route.meta
注意路由元信息的以下情况
1 父路由绑定的meta数据,子路由对应的组件能否拿到?--->不行
2 子路由绑定的meta数据,父路由对应的组件能否拿到?---》可以,但是路径必须访问到目标子路由
为了让父组件中能够实时的更新渲染子路由的meta值(切换子路由时),就可以通过computed来实现
父:
{{name}}
computed:{
name(){
return this.$route.meta.name
}
},
路由文件:
{
path: "/main",
component: Main,
children:[
{
path: "stuList",
component: StuList,
meta:{
name:"学生列表",
},
},
{
path: "stuAdd",
component: StuAdd,
meta:{
name:"学生添加",
},
},
]
关于router-link标签的属性
1 active-class 被激活时拥有xx样式
<router-link active-class="c" to="/main/stuList">
2 tag 伪装成其他标签
<router-link tag="button"
3 不会记录历史(只影响浏览器的回退按钮)
<router-link replace>
第四节、Vuex状态管理
vue全家桶项目:核心 + router路由 +vuex状态管理
概念
vuex:状态管理模式(简称为状态机)
状态:vue中使用到的数据
换句话说:vuex就是用来管理数据的仓库,有项目项目中所有公共的数据(属性、方法)都可以交给vuex来管理,
使用场景:多个组件都在共享某个数据时,比如userInfo
安装
随着vue全家桶一并安装
结构
src/store/indes.js
//引入Vue
import Vue from 'vue'
//引入vuex
import Vuex from 'vuex'
//使用Vuex
Vue.use(Vuex)
//暴露仓库对象(store)
export default new Vuex.Store({
//公共属性
state: {
},
//公共计算属性
getters: {
},
//公共方法(同步)
mutations: {
},
//公共方法(异步)
actions: {
},
//分模块
modules: {
}
})
src/main.js
import store from './store'
new Vue({
//注入到Vue实例,意味着我们会今后可以在任意组件中通过this.$router/$route来获取路由对象
router,
//注入到vue实例,意味着我们会今后可以在任意组件中通过this.$store来获取仓库对象
store,
render: h => h(App)
}).$mount('#app')
vuex核心属性
1 state
该属性用来存储公共数据(指的是属性)
index.js
export default new Vuex.Store({
//公共属性
state: {
name:"张三",
},
组件中获取:this.$store.state
一定要搭配computed,才能够实时的拿到最新仓库中值
vue:
<template>
<div>
{{name}}
</div>
</template>
<script>
export default {
computed:{
name(){
return this.$store.state.name
}
}
}
</script>
2 getters
该属性用来存储公共的计算属性的
index.js
//暴露仓库对象(store)
export default new Vuex.Store({
//公共属性
state: {
n1:1,
n2:2
},
//公共计算属性
getters: {
//就是上方的state对象
sum(state){
return state.n1+state.n2
}
},
vue:
{{sum}}
computed:{
sum(){
return this.$store.getters.sum
}
}
3 mutations
该属性是用来存储同步方法,而方法一般都是用来改变state值的方法,这也是更改state值唯一的途径
index.js
export default new Vuex.Store({
//公共属性
state: {
name:"张三"
},
//公共方法(同步)
mutations: {
//参数1:state对象
//参数2:获取传递过来的参数
change_name(state,params){
state.name=params
}
},
vue:
需要通过store仓库对象.commit()方可调用vuex中mutations中定义的方法
//参数1:指向仓库中的mutations中的方法名
//参数2:想要传递的数据
this.$store.commit('change_name',参数)
4 actions
该属性用来存储异步方法居多,比如异步请求等
index.js
//参数1:整个仓库对象
//参数2:获取传递过来的参数
getClas(store,params){
return axios({
url:"/clas/getClas",
method:"get",
})
}
vue:
需要通过store仓库对象.dispatch()方可调用vuex中actions中定义的方法
this.$store.dispatch('getClas','参数')
demo1:在组件中调用公共actions方法拿到异步数据班级数组,然后再在组件中调用mutations方法为state的班级数组赋值
index.js
//引入Vue
import Vue from 'vue'
//引入vuex
import Vuex from 'vuex'
//使用Vuex
Vue.use(Vuex)
import axios from '@/http/axios.js'
//暴露仓库对象(store)
export default new Vuex.Store({
//公共属性
state: {
clas:[],
},
//公共方法(同步)
mutations: {
change_clas(state,params){
state.clas=params
}
},
//公共方法(异步)
actions: {
//查询班级
//参数1:整个仓库对象
//参数2:获取传递过来的参数
getClas(store,params){
return axios({
url:"/clas/getClas",
method:"get",
})
}
},
//分模块
modules: {
}
})
vue:
//参数1:指向仓库中的actions中的方法名
//参数2:想要传递的数据
let res=await this.$store.dispatch('getClas','参数')
this.$store.commit("change_clas",res.result)
以上的做法是不推荐的,组件中调用两次(1次是调用actions,1次是mutations)
demo2:
index.js
//引入Vue
import Vue from 'vue'
//引入vuex
import Vuex from 'vuex'
//使用Vuex
Vue.use(Vuex)
import axios from '@/http/axios.js'
//暴露仓库对象(store)
export default new Vuex.Store({
//公共属性
state: {
clas:[],
},
//公共方法(同步)
mutations: {
change_clas(state,params){
state.clas=params
}
},
//公共方法(异步)
actions: {
//查询班级
//参数1:整个仓库对象 ==组件中的this.$store
//参数2:获取传递过来的参数
async getClas(store,params){
let res=await axios({
url:"/clas/getClas",
method:"get",
})
//调用mutations方法把数据传递给state
store.commit('change_clas',res.result)
}
},
//分模块
modules: {
}
})
vue:
this.$store.dispatch('getClas','参数')
5 modules
为了对vuex进一步管理,需要分模块
1 store/下新建modules/模块.js,比如clas.js
2 编辑clas.js
import axios from '@/http/axios.js'
export default {
//命名空间设置true,避免getters、mutations、actions不同模块取名相同造成冲突
namespaced:true,
state:{
clas:[],
name:""
},
mutations:{
change_clas(state,params){
state.clas=params
}
},
actions:{
async getClas(store,params){
let res=await axios({
url:"/clas/getClas",
method:"get",
})
//调用mutations方法把数据传递给state
store.commit('change_clas',res.result)
},
}
}
3 store/index.js
//引入Vue
import Vue from 'vue'
//引入vuex
import Vuex from 'vuex'
//使用Vuex
Vue.use(Vuex)
import clas from './modules/clas.js'
//暴露仓库对象(store)
export default new Vuex.Store({
//分模块
modules: {
clas
}
})
4 在组件中使用模块化之后的store
4.1 在组件使用辅助函数
<template>
<div>
{{clas}}
</div>
</template>
<script>
import {createNamespacedHelpers} from 'vuex'
let {mapState,mapActions}=createNamespacedHelpers('clas')
export default {
computed:{
...mapState(['clas']),
},
methods:{
...mapActions(['getClas']),
},
created(){
this.getClas()
}
}
</script>
一个组件引入多个模块:
在解构时取别名:
<template>
<div>
{{clas}}--{{username}}
</div>
</template>
<script>
import {createNamespacedHelpers} from 'vuex'
let {mapState:mapState_clas,mapActions:mapActions_clas}=createNamespacedHelpers('clas')
let {mapState:mapState_user,}=createNamespacedHelpers('users')
export default {
computed:{
...mapState_clas(['clas']),
...mapState_user(['username'])
},
methods:{
...mapActions_clas(['getClas']),
},
created(){
this.getClas()
}
}
</script>
<style>
</style>
4.2 使用原生
为什么要讲原生的,因为辅助函数只能用在vue组件中,如果今后某些需求需要我们在js中操作仓库,只能用原生操作
import store from '@/store' //store等价于 this.$store
state: store.state.模块名.属性
getters: store.getters.模块名.属性
mutations: store.commit('模块名/mutations方法',参数)
actions: store.dispatch('模块名/actions方法',参数)
混入mixins
概念:能够把相同的属性提取出来,分别混入到指定的组件中
作用:组件对于混入的属性是不用自己编写,可以通过mixins引入那些属性
步骤
1 src下新建mixins/index.js
export default{
//组件中的一切属性
data(){
return {}
},
methods:{
},
watch:{},
computed:{},
钩子函数...
}
2 组件中引入定义的混入对象
import mymixins from '@/mixins/index.js'
export default {
mixins:[mymixins,.......],
混入mixins和状态机vuex的区别
-
1 操作数据
混入后,则混入对象只属于当前组件,无论怎么该,都只影响当前组件,对其他组件的混入数据没有任何影响
状态机: 如果当前组件更改了状态机的数据,则必然对另一个组件有影响 -
2 场景:
状态机使用率>>>混入:状态机能够时多个组件共享数据(实时获得最新值)
混入后多个组件的数据没有任何关系,因此也就无法共享 -
3 混入弊端:
无法知道混入后的数据来源