Vue2开发笔记

发布时间 2023-09-16 10:55:32作者: 灯火阑珊c

Vue 组件化开发笔记

环境安装

1、全局安装webpack

npm install webpack -g

2、全局安装vue脚手架

npm i -g @vue/cli-init

3、关于vue-cli2.x和vue-cli3.x创建项目的区别:

(1)vue-cli2.x创建项目:

vue init webpack 项目名称

(2)vue-cli3.x创建项目:

vue create 项目名称

4、启动项目

npm run dev

生命周期

在这里插入图片描述

Vue2笔记

CSS局部生效

scoped关键字

<style  scoped>

</style>

组件自定义事件

绑定事件监听

<Header @addTodo="addTodo"/>
或者
<Header ref="header"/>
this.$refs.header.$on('addTodo', this.addTodo)
触发事件
this.$emit('addTodo', todo)

全局事件总线

1. Vue 原型对象上包含事件处理的方法

1) $on(eventName, listener): 绑定自定义事件监听
2) $emit(eventName, data): 分发自定义事件
3) $off(eventName): 解绑自定义事件监听
4) $once(eventName, listener): 绑定事件监听, 但只能处理一次

2. 所有组件实例对象的原型对象的原型对象就是 Vue 的原型对象

1) 所有组件对象都能看到 Vue 原型对象上的属性和方法
2) Vue.prototype.$bus = new Vue(), 所有的组件对象都能看到$bus 这个属性
对象

3. 全局事件总线

1) 包含事件处理相关方法的对象(只有一个)
2) 所有的组件都可以得到

4. 全局事件总线操作

1 指定事件总线对象

new Vue({
	beforeCreate () { // 尽量早的执行挂载全局事件总线对象的操作
	Vue.prototype.$globalEventBus = this //globalEventBus为自定义名称
	},
}).$mount('##root')

2 绑定事件

this.$globalEventBus.$on('deleteTodo', this.deleteTodo)

3 分发事件

this.$globalEventBus.$emit('deleteTodo', this.index)

4 解绑事件

this.$globalEventBus.$off('deleteTodo')

在main.js中设置全局时间总线

new Vue({
 el: '##app',
 router,
 components: { App },
 //在模板解析前创建
 beforeCreate() {
 //vm及所有的vc都能看到
   Vue.prototype.$bus = this
 },
 template: '<App/>'
})

5. 消息订阅与发布

  1. 这种方式的思想与全局事件总线很相似
  2. 它包含以下操作:
    (1) 订阅消息 --对应绑定事件监听
    (2) 发布消息 --分发事件
    (3) 取消消息订阅 --解绑事件监听
  3. 需要引入一个消息订阅与发布的第三方实现库: PubSubJS:
1. 在线文档: https://github.com/mroderick/PubSubJS
2. 下载: npm install -S pubsub-js
3. 相关语法
(1) import PubSub from 'pubsub-js' // 引入
(2) PubSub.subscribe(‘msgName’, functon(msgName, data){ })
(3) PubSub.publish(‘msgName’, data): 发布消息, 触发订阅的回调函数调用
(4) PubSub.unsubscribe(token): 取消消息的订阅

$nextTick

  1. 语法:this.$nextTick(回调函数)
  2. 作用:在下一次DOM更新结束后执行其指定的回调
  3. 什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行。

过渡动画

1. 用transition包裹需要动画的元素

<transition name="hello" appear>
	<h1>hello</h1>
</transition>

2. 当多个元素时需要使用transition-group

引入第三方动画库
npm install animate.css
导入
import 'animate.css';
<transition-group
        name="animate__animated animate__bounce"
        appear
        enter-active-class="animate__headShake"
        leave-active-class="animate__backOutRight"
      >
        <MyItem v-for="todoObj in todos" :key="todoObj.id" :todo="todoObj" />
      </transition-group>

axios

1. 安装

npm install axios

2. 导入

import axios from "axios";

2.1 在main.js中引入axios并全局配置

//--------------------------axios------------------------------------
//在main.js中引入axios并全局配置
import axios from 'axios'
Vue.prototype.$axios = axios

3. GET请求

// 向给定ID的用户发起请求
axios.get('/user?ID=12345')
  .then((response) => {
    // 处理成功情况
    console.log(response);
  })
  .catch((error) => {
    // 处理错误情况
    console.log(error);
  })
  .then(function () {
    // 总是会执行
  });

// 上述请求也可以按以下方式完成(可选)
axios.get('/user', {
    params: {
      ID: 12345
    }
  })
  .then((response) => {
    console.log(response);
  })
  .catch((error) => {
    console.log(error);
  })
  .then(function () {
    // 总是会执行
  });  

// 支持async/await用法
async function getUser() {
  try {
    const response = await axios.get('/user?ID=12345');
    console.log(response);
  } catch (error) {
    console.error(error);
  }
}

4. POST请求

发起一个POST请求

axios.post('/user', {
    firstName: 'Fred',
    lastName: 'Flintstone'
  })
  .then((response) => {
    console.log(response);
  })
  .catch((error) => {
    console.log(error);
  });

发起多个并发请求

function getUserAccount() {
  return axios.get('/user/12345');
}

function getUserPermissions() {
  return axios.get('/user/12345/permissions');
}

Promise.all([getUserAccount(), getUserPermissions()])
  .then(function (results) {
    const acct = results[0];
    const perm = results[1];
  });

vue-resource插件

1. 安装

npm i vue-resource

2. 引入并使用插件

//引入
import vueResource from 'vue-resource'
//使用插件
Vue.use(vueResource)

3. 发送请求

this.$http.get() //post等请求方法与axios一致

引入ElementUI

1 安装

npm i element-ui -S

2 main.js中引入

//---------引入Element-UI---------
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);
//--------------------------------

代理(可用于前端解决跨域)

1. vue.config.js 配置

devServer.proxy 可以是一个指向开发环境 API 服务器的字符串:

module.exports = {
  devServer: {
    proxy: 'http://localhost:4000'
  }
}

这会告诉开发服务器将任何未知请求 (没有匹配到静态文件的请求) 代理到http://localhost:4000。

如果你想要更多的代理控制行为,也可以使用一个 path: options 成对的对象。
完整的选项可以查阅 http-proxy-middleware

module.exports = {
  devServer: {
    proxy: {
      '/api': {
        target: '<url>',	//后端地址
        ws: true,	//用于websocket
        changeOrigin: true	//是否跨域(端口欺骗)
      },
      '/foo': {
        target: '<other_url>'
      }
    }
  }
}

2. config文件夹下的index.js文件找到dev里面的proxyTable{}

proxyTable: {
  "/api": {
         target: "<url>", //ip地址后端给的不变
         changeOrigin: true, //是否跨域
         pathRewrite: {
           "^/api": "" //重写为空,这个时候api就相当于上面target接口基准地址
         },
       }
}



插槽

1.默认插槽

1.Student组件中声明插槽

<slot>这是一个插槽,等着被填充内容</slot>

2.App使用组件并填充内容

<Student>
<!-- 此时img标签将会放入Student组件slot中 -->
<img src="abc.png" alt="">
</Student>

2.具名插槽

1.Student组件中声明插槽并追加name

<slot name="top">这是一个插槽,等着被填充内容</slot>

2.App使用组件并填充内容

<Student>
  <!-- 此时img标签将会放入Student组件名为top的slot中 -->
  <img slot="top" src="abc.png" alt="">

  <!-- 当有多个标签时推荐使用template包裹,有两种写法,v-slot只能搭配template使用 -->
  <template v-slot:top>
  <template slot="top">
    <img  src="abc.png" alt="">
    <a href="http://www.abc.com">abc.com</a>
  </template>
</Student>

3.作用域插槽

1.Student组件中声明插槽并传递data数据

<slot name="top" :message="message">这是一个插槽,等着被填充内容</slot>

2.App使用组件并填充内容
scope接收一个或多组数据并封装

<Student slot="top" title="学生信息">
  <template scope="scopeData">
	<ul>
		<li v-for="(m,index) in scopeData.message" :key="index">{{m}}</li>
	</ul>
  </template>
</Student>

如果只接收具体数据,可解构

<Student slot="top" title="学生信息">
  <template scope="{message}">
	<ul>
		<li v-for="(m,index) in message" :key="index">{{m}}</li>
	</ul>
  </template>
</Student>

vuex

1. 工作原理

vuex工作原理

2.使用Vuex

1. 安装Vuex

最新版本只能在vue3中使用,vue2中只能使用vuex的3版本

npm i vuex@3

2. 环境搭建

  1. 创建文件:src/store/index.js
//该文件用于创建Vuex的store
//引入Vue
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//使用Vuex插件
Vue.use(Vuex)
//准备actions用于响应组件动作
const actions = {}
//准备mutations用于操作数据(state)
const mutations = {}
//准备state用于存储数据
const state = {}
//创建并暴露store
export default new Vuex.Store({
    actions,
    mutations,
    state,
})
  1. main.js中创建vm时传入store配置项
//引入store
import store from './store/index'

new Vue({
  el: '#app',
  router,
  store, //vm配置store
  components: { App },
  beforeCreate() {
    Vue.prototype.$bus = this
  },
  template: '<App/>'
})

3. 基本使用

  1. 初始化数据、配置actions、配置mutations,操作文件store.js

    //引入Vue核心库
    import Vue from 'vue'
    //引入Vuex
    import Vuex from 'vuex'
    //引用Vuex
    Vue.use(Vuex)
    
    const actions = {
        //响应组件中加的动作
    	jia(context,value){
    		// console.log('actions中的jia被调用了',miniStore,value)
    		context.commit('JIA',value)
    	},
    }
    
    const mutations = {
        //执行加
    	JIA(state,value){
    		// console.log('mutations中的JIA被调用了',state,value)
    		state.sum += value
    	}
    }
    
    //初始化数据
    const state = {
       sum:0
    }
    
    //创建并暴露store
    export default new Vuex.Store({
    	actions,
    	mutations,
    	state,
    })
    
  2. 组件中读取vuex中的数据:$store.state.sum

  3. 组件中修改vuex中的数据:$store.dispatch('action中的方法名',数据)$store.commit('mutations中的方法名',数据)

    备注:若没有网络请求或其他业务逻辑,组件中也可以越过actions,即不写dispatch,直接编写commit

4. getters的使用

  1. 概念:当state中的数据需要经过加工后再使用时,可以使用getters加工。

  2. store.js中追加getters配置

    ......
    
    const getters = {
    	bigSum(state){
    		return state.sum * 10
    	}
    }
    
    //创建并暴露store
    export default new Vuex.Store({
    	......
    	getters
    })
    
  3. 组件中读取数据:$store.getters.bigSum

5. 四个map方法的使用

  1. 组件中引入相关map方法

    //组件中引入mapState,mapGetters,mapActions,mapMutations
    import {mapState,mapGetters,mapActions,mapMutations} from 'vuex'
    
  2. mapState方法:用于帮助我们映射state中的数据为计算属性

    computed: {
    	//...是ES6写法,展开对象放入computed
        //借助mapState生成计算属性:sum、school、subject(对象写法)
         ...mapState({sum:'sum',school:'school',subject:'subject'}),
             
        //借助mapState生成计算属性:sum、school、subject(数组写法)
        ...mapState(['sum','school','subject']),
    },
    
  3. mapGetters方法:用于帮助我们映射getters中的数据为计算属性

    computed: {
        //借助mapGetters生成计算属性:bigSum(对象写法)
        ...mapGetters({bigSum:'bigSum'}),
    
        //借助mapGetters生成计算属性:bigSum(数组写法)
        ...mapGetters(['bigSum'])
    },
    
  4. mapActions方法:用于帮助我们生成与actions对话的方法,即:包含$store.dispatch(xxx)的函数

    methods:{
        //靠mapActions生成:incrementOdd、incrementWait(对象形式)
        ...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
    
        //靠mapActions生成:incrementOdd、incrementWait(数组形式)
        ...mapActions(['jiaOdd','jiaWait'])
    }
    
    1. mapMutations方法:用于帮助我们生成与mutations对话的方法,即:包含$store.commit(xxx)的函数
    methods:{
        //靠mapActions生成:increment、decrement(对象形式)
        ...mapMutations({increment:'JIA',decrement:'JIAN'}),
        
        //靠mapMutations生成:JIA、JIAN(对象形式)
        ...mapMutations(['JIA','JIAN']),
    }
    

备注:mapActions与mapMutations使用时,若需要传递参数需要:在模板中绑定事件时传递好参数,否则参数是事件对象。

6. 模块化+命名空间

  1. 目的:让代码更好维护,让多种数据分类更加明确。

  2. 修改store.js

    const countAbout = {
      namespaced:true,//开启命名空间
      state:{x:1},
    	actions: { ... },
      mutations: { ... },
      getters: {
        bigSum(state){
           return state.sum * 10
        }
      }
    }
    
    const personAbout = {
      namespaced:true,//开启命名空间
      state:{ ... },
    	actions: { ... },
      mutations: { ... },
    }
    
    const store = new Vuex.Store({
      modules: {
        countAbout,
        personAbout
      }
    })
    
  3. 开启命名空间后,组件中读取state数据:

    //方式一:自己直接读取
    this.$store.state.personAbout.list
    //方式二:借助mapState读取:
    ...mapState('countAbout',['sum','school','subject']),
    
  4. 开启命名空间后,组件中读取getters数据:

    //方式一:自己直接读取
    this.$store.getters['personAbout/firstPersonName']
    //方式二:借助mapGetters读取:
    ...mapGetters('countAbout',['bigSum'])
    
  5. 开启命名空间后,组件中调用dispatch

    //方式一:自己直接dispatch
    this.$store.dispatch('personAbout/addPersonWang',person)
    //方式二:借助mapActions:
    ...mapActions('countAbout',{incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
    
  6. 开启命名空间后,组件中调用commit

    //方式一:自己直接commit
    this.$store.commit('personAbout/ADD_PERSON',person)
    //方式二:借助mapMutations:
    ...mapMutations('countAbout',{increment:'JIA',decrement:'JIAN'}),
    

vue-router

  1. 路由组件通常存放在pages文件夹,一般组件通常存放在components文件夹。
  2. 通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载。
  3. 每个组件都有自己的$route属性,里面存储着自己的路由信息。
  4. 整个应用只有一个router,可以通过组件的$router属性获取到。

1.安装

最新版本vue-router需要Vue3才能使用,vue2安装3版本

vue2:

npm i vue-router@3

2.编写router配置项:

//引入VueRouter
import VueRouter from 'vue-router'
//引入Luyou 组件
import About from '../components/About'
import Home from '../components/Home'

//创建并暴露router实例对象,去管理一组一组的路由规则
export default new VueRouter({
	routes:[
		{
			path:'/about',
			component:About
		},
		{
			path:'/home',
			component:Home
		}
	]
})

3.实现切换(active-class可配置高亮样式)

<router-link active-class="active" to="/about">About</router-link>

4.指定展示位置

<router-view></router-view>

5.多级路由(嵌套路由)

  1. 配置路由规则,使用children配置项:

    routes:[
    	{
    		path:'/about',
    		component:About,
    	},
    	{
    		path:'/home',
    		component:Home,
    		children:[ //通过children配置子级路由
    			{
    				path:'news', //此处一定不要写:/news
    				component:News
    			},
    			{
    				path:'message',//此处一定不要写:/message
    				component:Message
    			}
    		]
    	}
    ]
    
  2. 跳转(要写完整路径):

    <router-link to="/home/news">News</router-link>
    

6.命名路由(简化跳转)

  1. 作用:可以简化路由的跳转。

  2. 如何使用

    1. 给路由命名:
    {
    	path:'/demo',
    	component:Demo,
    	children:[
    		{
    			path:'test',
    			component:Test,
    			children:[
    				{
                        name:'hello' //给路由命名
    					path:'welcome',
    					component:Hello,
    				}
    			]
    		}
    	]
    }
    
    1. 简化跳转:
    <!--简化前,需要写完整的路径 -->
    <router-link to="/demo/test/welcome">跳转</router-link>
    
    <!--简化后,直接通过名字跳转 -->
    <router-link :to="{name:'hello'}">跳转</router-link>
    
    <!--简化写法配合传递参数 -->
    <router-link 
    	:to="{
    		name:'hello',
    		query:{
    		   id:666,
                title:'你好'
    		}
    	}"
    >跳转</router-link>
    

7.路由传参

1.路由的query参数

  1. 传递参数

    <!-- 跳转并携带query参数,to的字符串写法 -->
    <router-link :to="/home/message/detail?id=666&title=你好">跳转</router-link>
    				
    <!-- 跳转并携带query参数,to的对象写法 -->
    <router-link 
    	:to="{
    		path:'/home/message/detail',
    		query:{
    		   id:666,
                title:'你好'
    		}
    	}"
    >跳转</router-link>
    
  2. 接收参数:

    $route.query.id
    $route.query.title
    

2.路由的params参数(path)

  1. 配置路由,声明接收params参数

    {
    	path:'/home',
    	component:Home,
    	children:[
    		{
    			path:'news',
    			component:News
    		},
    		{
    			component:Message,
    			children:[
    				{
    					name:'xiangqing',
    					path:'detail/:id/:title', //使用占位符声明接收params参数
    					component:Detail
    				}
    			]
    		}
    	]
    }
    
  2. 传递参数

    <!-- 跳转并携带params参数,to的字符串写法 -->
    <router-link :to="/home/message/detail/666/你好">跳转</router-link>
    				
    <!-- 跳转并携带params参数,to的对象写法 -->
    <router-link 
    	:to="{
    		name:'xiangqing',
    		params:{
    		   id:666,
                title:'你好'
    		}
    	}"
    >跳转</router-link>
    

    特别注意:路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置!

  3. 接收参数:

    $route.params.id
    $route.params.title
    

8.路由的props配置

作用:让路由组件更方便的收到参数
{
	name:'xiangqing',
	path:'detail/:id',
	component:Detail,

	//第一种写法:props值为对象,该对象中所有的key-value的组合最终都会通过props传给Detail组件
	// props:{a:900}

	//第二种写法:props值为布尔值,布尔值为true,则把路由收到的所有params参数通过props传给Detail组件
	// props:true
	
	//第三种写法:props值为函数,该函数返回的对象中每一组key-value都会通过props传给Detail组件
	props(route){
		return {
			id:route.query.id,
			title:route.query.title
		}
	}
}

9.<router-link>的replace属性

  1. 作用:控制路由跳转时操作浏览器历史记录的模式
  2. 浏览器的历史记录有两种写入方式:分别为pushreplacepush是追加历史记录,replace是替换当前记录。路由跳转时候默认为push
  3. 如何开启replace模式:<router-link replace .......>News</router-link>

10.编程式路由导航

  1. 作用:不借助<router-link> 实现路由跳转,让路由跳转更加灵活

  2. 具体编码:

    //$router的两个API
    this.$router.push({
    	name:'xiangqing',
    		params:{
    			id:xxx,
    			title:xxx
    		}
    })
    
    this.$router.replace({
    	name:'xiangqing',
    		params:{
    			id:xxx,
    			title:xxx
    		}
    })
    this.$router.forward() //前进
    this.$router.back() //后退
    this.$router.go() //可前进也可后退
    

11.缓存路由组件

  1. 作用:让不展示的路由组件保持挂载,不被销毁。

  2. 具体编码:

    <keep-alive include="News">  指定缓存News组件
        <router-view></router-view>
    </keep-alive>
    

12.两个新的生命周期钩子

  1. 作用:路由组件所独有的两个钩子,用于捕获路由组件的激活状态。
  2. 具体名字:
    1. activated路由组件被激活时触发。
    2. deactivated路由组件失活时触发。

13.路由守卫

  1. 作用:对路由进行权限控制

  2. 分类:全局守卫、独享守卫、组件内守卫

  3. 全局守卫:

    //src/router/index.js
    const router = new VueRouter({
    	routes:[
    		{
    			name:'guanyu',
    			path:'/about',
    			component:About,
    			meta:{isAuth:true}
    		},
    		{
    			name:'zhuye',
    			path:'/home',
    			component:Home,
    			meta:{isAuth:true}
    		}
    	]
    })
    //全局前置守卫:初始化时执行、每次路由切换前执行
    router.beforeEach((to,from,next)=>{
    	console.log('beforeEach',to,from)
    	if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制
    		if(localStorage.getItem('school') === 'atguigu'){ //权限控制的具体规则
    			next() //放行
    		}else{
    			alert('暂无权限查看')
    			// next({name:'guanyu'})
    		}
    	}else{
    		next() //放行
    	}
    })
    
    //全局后置守卫:初始化时执行、每次路由切换后执行
    router.afterEach((to,from)=>{
    	console.log('afterEach',to,from)
    	if(to.meta.title){ 
    		document.title = to.meta.title //修改网页的title
    	}else{
    		document.title = 'vue_test'
    	}
    })
    //暴露
    export default router
    
  4. 独享守卫:

    //src/router/index.js
    //独享守卫
    //beforeEnter(to,from,next){}
    const router = new VueRouter({
    	routes:[
    		{
    			name:'guanyu',
    			path:'/about',
    			component:About,
    			meta:{isAuth:true}
    		},
    		{
    			name:'zhuye',
    			path:'/home',
    			component:Home,
    			meta:{isAuth:true}
    			beforeEnter(to,from,next){
    				console.log('beforeEnter',to,from)
    				if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制(路由配置项中自定义的数据可放入meta对象中)
    					if(localStorage.getItem('school') === 'atguigu'){
    						next()
    					}else{
    						alert('暂无权限查看')
    						// next({name:'guanyu'})
    					}
    				}else{
    					next()
    				}
    			}
    			}
    		]
    	})
    
    
  5. 组件内守卫:

    //xxx.vue
    //进入守卫:通过路由规则,进入该组件时被调用
    beforeRouteEnter (to, from, next) {
    },
    //离开守卫:通过路由规则,离开该组件时被调用
    beforeRouteLeave (to, from, next) {
    }
    

14.路由器的两种工作模式

  1. 对于一个url来说,什么是hash值?—— #及其后面的内容就是hash值。

  2. hash值不会包含在 HTTP 请求中,即:hash值不会带给服务器。

  3. hash模式:

    1. 地址中永远带着#号,不美观 。
    2. 若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法。
    3. 兼容性较好。
  4. history模式:

    1. 地址干净,美观 。
    2. 兼容性和hash模式相比略差。
    3. 应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题。
  5. 修改工作模式

    //router中配置mode属性
    mode:'hash' //hash、history
    

15.命名视图(router-view)

有时候想同时 (同级) 展示多个视图,而不是嵌套展示,例如创建一个布局,有 sidebar(侧导航) 和 main(主内容) 两个视图,这个时候命名视图就派上用场了。你可以在界面中拥有多个单独命名的视图,而不是只有一个单独的出口。如果 router-view没有设置名字,那么默认为 default

<router-view class="view left-sidebar" name="LeftSidebar"></router-view>
<router-view class="view main-content"></router-view>
<router-view class="view right-sidebar" name="RightSidebar"></router-view>

一个视图使用一个组件渲染,因此对于同个路由,多个视图就需要多个组件。确保正确使用 components 配置 (带上 s):

const router = createRouter({
  history: createWebHashHistory(),
  routes: [
    {
      path: '/',
      components: {
        default: Home,
        // LeftSidebar: LeftSidebar 的缩写
        LeftSidebar,
        // 它们与 `<router-view>` 上的 `name` 属性匹配
        RightSidebar,
      },
    },
  ],
})

嵌套命名视图

我们也有可能使用命名视图创建嵌套视图的复杂布局。这时你也需要命名用到的嵌套 router-view 组件。我们以一个设置面板为例:

/settings/emails                                       /settings/profile
+-----------------------------------+                  +------------------------------+
| UserSettings                      |                  | UserSettings                 |
| +-----+-------------------------+ |                  | +-----+--------------------+ |
| | Nav | UserEmailsSubscriptions | |  +------------>  | | Nav | UserProfile        | |
| |     +-------------------------+ |                  | |     +--------------------+ |
| |     |                         | |                  | |     | UserProfilePreview | |
| +-----+-------------------------+ |                  | +-----+--------------------+ |
+-----------------------------------+                  +------------------------------+
  • Nav 只是一个常规组件。
  • UserSettings 是一个视图组件。
  • UserEmailsSubscriptionsUserProfileUserProfilePreview 是嵌套的视图组件。

注意:我们先忘记 HTML/CSS 具体的布局的样子,只专注在用到的组件上。

UserSettings 组件的 <template> 部分应该是类似下面的这段代码:

<!-- UserSettings.vue -->
<div>
  <h1>User Settings</h1>
  <NavBar />
  <router-view />
  <router-view name="helper" />
</div>

那么你就可以通过这个路由配置来实现上面的布局:

{
  path: '/settings',
  // 你也可以在顶级路由就配置命名视图
  component: UserSettings,
  children: [{
    path: 'emails',
    component: UserEmailsSubscriptions
  }, {
    path: 'profile',
    components: {
      default: UserProfile,
      helper: UserProfilePreview
    }
  }]
}

16.重定向和别名

  1. 重定向
    重定向也是通过 routes 配置来完成,下面例子是从 /home 重定向到 /

    const routes = [{ path: '/home', redirect: '/' }]
    

    重定向的目标也可以是一个命名的路由:

    const routes = [{ path: '/home', redirect: { name: 'homepage' } }]
    

    甚至是一个方法,动态返回重定向目标:

    const routes = [
      {
        // /search/screens -> /search?q=screens
        path: '/search/:searchText',
        redirect: to => {
          // 方法接收目标路由作为参数
          // return 重定向的字符串路径/路径对象
          return { path: '/search', query: { q: to.params.searchText } }
        },
      },
      {
        path: '/search',
        // ...
      },
    ]
    

    请注意,导航守卫并没有应用在跳转路由上,而仅仅应用在其目标上。在上面的例子中,在 /home 路由中添加 beforeEnter 守卫不会有任何效果。

    在写 redirect 的时候,可以省略 component 配置,因为它从来没有被直接访问过,所以没有组件要渲染。唯一的例外是嵌套路由:如果一个路由记录有 childrenredirect 属性,它也应该有 component 属性。

  2. 相对重定向
    也可以重定向到相对位置:

    const routes = [
      {
        // 将总是把/users/123/posts重定向到/users/123/profile。
        path: '/users/:id/posts',
        redirect: to => {
          // 该函数接收目标路由作为参数
          // 相对位置不以`/`开头
          // 或 { path: 'profile'}
          return 'profile'
        },
      },
    ]
    
  3. 别名
    重定向是指当用户访问 /home 时,URL 会被 / 替换,然后匹配成 /。那么什么是别名呢?

    / 别名为 /home,意味着当用户访问 /home 时,URL 仍然是 /home,但会被匹配为用户正在访问 /

    上面对应的路由配置为:

    const routes = [{ path: '/', component: Homepage, alias: '/home' }]
    

    通过别名,你可以自由地将 UI 结构映射到一个任意的 URL,而不受配置的嵌套结构的限制。使别名以/ 开头,以使嵌套路径中的路径成为绝对路径。你甚至可以将两者结合起来,用一个数组提供多个别名:

    const routes = [
      {
        path: '/users',
        component: UsersLayout,
        children: [
          // 为这 3 个 URL 呈现 UserList
          // - /users
          // - /users/list
          // - /people
          { path: '', component: UserList, alias: ['/people', 'list'] },
        ],
      },
    ]
    

    如果你的路由有参数,请确保在任何绝对别名中包含它们:

    const routes = [
      {
        path: '/users/:id',
        component: UsersByIdLayout,
        children: [
          // 为这 3 个 URL 呈现 UserDetails
          // - /users/24
          // - /users/24/profile
          // - /24
          { path: 'profile', component: UserDetails, alias: ['/:id', ''] },
        ],
      },
    ]
    

    关于 SEO 的注意事项: 使用别名时,一定要定义规范链接.


Vue UI 组件库

1.移动端

  1. Vant https://youzan.github.io/vant
  2. Cube UI https://didi.github.io/cube-ui
  3. Mint UI http://mint-ui.github.io

2.PC端

  1. Element UI https://element.eleme.cn
  2. IView UI https://www.iviewui.com