vue必知必会,Vue在工程项目中常用知识点梳理与总结。

Vue常用知识点梳理

  • 一、数据双向绑定
    • 1.1 对象
    • 1.2 数组
    • 1.3 异步更新队列
    • 1.4 计算属性和数据监听
  • 二、组件间通信
    • 2.1 父子组件之间通信
      • 2.1.1 父组件向子组件传值(prop)
      • 2.1.2 父子组件间方法调用(`$refs`和`$emit`)
    • 2.2 兄弟组件(平行组件)间传值
      • 2.2.1 vuex
      • 2.2.2 $bus
    • 2.3 组件间通信注意事项
  • 三. axios 服务请求
    • 3.1 基本请求配置
    • 3.2 通用型配置示例
  • 四、vue路由router
    • 4.1. 路由跳转方式
    • 4.2 动态路由匹配
      • 4.2.1 动态路由匹配及路由传参
    • 4.3 路由组件传参
  • 五、Vue工程项目相关配置

一、数据双向绑定

数据双向绑定给我们开发带来了方便,在vue官方文档中单独用了一节来介绍响应式原理,需要深入了解的可以移步官网。下面仅梳理一下常用的数据变化处理方式。
注:Vue不允许动态添加根级响应式,因此响应式的对象处理,最好是在data中进行声明,并赋予初始值或空值(’’)。

1.1 对象

<span>{
     {
     obj.a}}</span>
/* 声明一个对象,用于举例说明*/
data(){
     
	return {
     
		obj = {
      
	 		"value_a":'a'
		}
	}
}
/*
 1. 修改已存在的对象属性值,使视图中的值也发生变化(响应式):
 	obj.value_a = 'a_change'   => value_a在data中声明时,已经存在,直接使用"."的方式,就可以达到响应式效果
 2. 修改新的未存在的对象属性值,视图中的值无法同步改变(非响应式):
 	obj.value_b = 'b'     => value_b在data中声明时未存在,使用这种方式,只能驱动当前对象的数值变化,不能使视图发生变化。
 3. 修改新的未存在的对象属性值,使视图中的值也发生变化(响应式):
 	Vue.set(vm.obj, 'value_b ', 'b_change')  
 	或者 使用Vue.set的别名vm.$set :this.$set(this.obj, 'value_b ', 'b_change') 
 	或者 使用ES6语法的对象结构:this.obj = {...this.obj,value_b:'b_change'}
 4. 使用新的对象,来为旧对象赋予多个属性
	 let newObj = {
		"value_c":"c",
		"value_d":"d",
		"value_e":"e"
	 }
 非响应式的做法 => Object.assign(this.obj,newObj )
 响应式的做法 => 即新旧对象一起合并为一个对象,再返回给旧对象。this.obj = Object.assign({},this.obj,newObj )
*/

① 方式2常用于方法体内作临时变量,给对象的属性赋值时,写法更方便。
② 若对象用于视图,则常采用方式1和3来驱动数据。
③ 方式4,在动态生成数据表格时比较适用,比如数据表格中有一些固定列和以日期区间变化而来的动态列,那么就可以用方法4,使新旧列名对象混合来达到动态表格的效果。

1.2 数组

<span v-for="(item,index) in arr" :key="index">{
     {
     item.name}}</span>
/* 声明一个对象,用于举例说明*/
data(){
     
	return{
     
		arr = [
	 		{
     "id":1,"name":"a"},
	 		{
     "id":2,"name":"b"},
	 		{
     "id":3,"name":"c"}
		]
	}
}
/*
1.修改数组中的某一项:
let changeIndex = 0
let newValue= {"id":1,"name":"a_change"}  
非响应式 => this.arr[0] = newVal 
响应式   => Vue.set(vm.arr, changeIndex , newValue)    或者
            this.$set(this.arr,changeIndex ,newValue ) 或者
            this.items.splice(changeIndex , 1, newValue)  
2.修改数组长度(目前好像还没有直接操作过数组长度)
非响应式 => vm.arr.length = 2 
响应式   => vm.arr.splice(2)
*/

数组的变异和非变异方法,参考官方网站:数组更新检测

1.3 异步更新队列

为了在数据变化之后等待 Vue 完成更新 DOM,可以在数据变化之后立即使用 Vue.nextTick(callback)。参考官网:异步更新队列

data(){
     
	return {
     
		message:'未更新'
	}
}
methods: {
     
 	/* 写法 1 */
   updateMessage () {
     
     this.message = '已更新'
     console.log(this.$el.textContent) // => '未更新'
     this.$nextTick(function () {
     
       console.log(this.$el.textContent) // => '已更新'
     })
     /* 或者 */
     /* this.$nextTick(()=> {
       console.log(this.$el.textContent) // => '已更新'
     })
     */
   },
   /* 写法 2 使用新的 ES2017 async/await  */
   async updateMessage () {
     
     this.message = '已更新'
     console.log(this.$el.textContent) // => '未更新'
     console.log(this.$el.textContent) // => '未更新'
     await this.$nextTick()
     console.log(this.$el.textContent) // => '已更新'
   }
}

1.4 计算属性和数据监听

computed计算属性,计算属性重新计算的前提是,用于计算的数据必须是双向绑定的数据,否则计算属性在首次加载后,是不会再主动更新数据的。
计算属性computed和侦听器watch的选择,尽量用计算属性,但也不是必须的选择。

二、组件间通信

/* 使用4个组件来说明 */
<A ref="ref_A">
	<A_child1 ref="ref_A_child1"></A_child1>
	<A_child2 ref="ref_A_child1"></A_child2>
</A>
<B ref="ref_B"></B>

组件关系解释:

  1. 父子关系:
    组件A <- 组件A_child1
    组件A <- 组件A_child2
  2. 兄弟关系:
    组件A <=> 组件B
    组件A_child1<=> 组件A_child2

2.1 父子组件之间通信

使用技能:prop$emit$refs
父子关系:
组件A <- 组件A_child1
组件A <- 组件A_child2

2.1.1 父组件向子组件传值(prop)

子组件可以使用prop属性来接收值。(参考:传递静态或动态Prop)
示例如下:

// 组件A
<template>
	<B :title="params.title"></B>
</template>
<script>
	import B from '@/B' 
	export default{
     
		name:"A",
		data() {
     
            return{
     
               params:{
     
               		"title":''
               }
            }
        },
         methods:{
     
            getTitle(val){
     
            	// 获取新的title,改变父组件的title值以作它用
                this.params.title = val  
            },
       	}
	}
</script>
// 组件B
<template>
	<input v-model="title"/>
	<button @click="submit"></button>
</template>
<script>
	import B from '@/B' 
	export default{
     
		name:"B",
		props:{
     
			initTitle: String
		}
		data() {
     
            return{
     
               title: this.initTitle
            }
        },
         methods:{
     
            submit(){
     
                this.$emit('getTitle',this.title)
            },
       	}
	}
</script>

流程示意图如下:
vue必知必会,Vue在工程项目中常用知识点梳理与总结。_第1张图片
注意: 在 JavaScript 中对象和数组是通过引用传入的,所以对于一个数组或对象类型的 prop 来说,在子组件中改变变更这个对象或数组本身将会影响到父组件的状态

情况一: 对于上面这种只是先进行附初始值,在用户确定之后,改变父组件值;用户取消时,父组件中的值不发生改变的流程,最好在使用prop传值时,要在子组件中生成时(created或mounted钩子中)进行深度拷贝
提供两种方式:
组数:this.arr = Object.assign([],this.arr_A); // arr_A是从父组件接收的prop值
对象:this.obj = Object.assign({},this.obj_A); // obj_A是从父组件接收的prop值
情况二: 父组件的值需要随着子组件中的值的修改而变化时,在子组件中直接使用父组件传过来的值进行操作即可,记住操作时要使用响应式的数据操作方法。
特别注意: 如果父组件A中传到子组件的值,在组件A本身也会有修改,那么在引用B组件时,要对B组件加上key,来保证每次B组件都会被重新构建和生成。 否则Vue会缓存B组件,而导致B的生命周期只有一次,也即B中只显示了父组件第一次传过来的值。

2.1.2 父子组件间方法调用($refs$emit)

  1. 父调子
    在组件上加上ref,父组件调用子组件的方法:使用this.$refs.child_refName.[属性 | 方法]来实现
<A ref="ref_A">
	<A_child1 ref="ref_A_child1"></A_child1>
	<A_child2 ref="ref_A_child1"></A_child2>
</A>
/*
父调子方法或属性:
调用子组件的方法:this.$refs.ref_A_child1.updateTtile("new Title")
调用子组件的数据:this.$refs.ref_A_child1.title = "new Title"
如果ref名称是动态的话,也可以写成对象的动态获取形式:
let ref_name = 'ref_A_child1'
this.$refs[ref_name].updateTtile("new Title")
*/
  1. 子调父
    在子组件中使用this.$emit('method_name',args)的方式进行,举例如下:
/* A组件 */
<A>
	<A_child1 ref="ref_A_child1" @event_name='updateName'></A_child1>
	<A_child2 ref="ref_A_child1"></A_child2>
</A>
methods:{
     
	updateName(val){
     
		this.title = val
	}
}
/* A_child1组件  */
methods:{
     
	let title = 'new title'
	this.$emit('event_name',title)
}

注: event_name是父组件在引用子组件时声明的自定义事件,并非直接使用父组件methods的方法。

2.2 兄弟组件(平行组件)间传值

使用技能:vuex 和 $bus
兄弟关系:
组件A <=> 组件B
组件A_child1<=> 组件A_child2
prop属性一样,vuex可以用来替换prop传值。
$emit一样,$bus总线可以用来组件对事件的监听

2.2.1 vuex

vuex就像是声明了一个全局变量一样,使用vuex的仓库,可以代替全局变量。同时,vuex配合mutations使用,可以帮助我们跟踪值的变化过程,当然,如果值的操作比较简单,也可以直接当作全局变量使用。
如何使用vuex呢?下面给出案例:
在vue项目的src目录下,创建store文件夹(./src/store),分别在store下创建三个文件index.js(./src/store/index.js)、mutations.js(./src/store/mutations.js)、state.js(./src/store/state.js)。

  1. index.js文件如下
/* index.js */ 
import Vue from 'vue'
import Vuex from 'vuex'
import mutations from './mutations.js'
import state from './state.js'

Vue.use(Vuex);

export default new Vuex.Store({
     
	state, mutations
})
  1. mutations.js(跟踪值变化过程)
/* mutations.js */ 
const mutations = {
     
  // 设置侧边栏菜单是否收起isMenuClose的值
  setTitle(state, data) {
     
    state.title = data;
  },
}
export default mutations
  1. state.js(声明变量)
/* state.js */
const state = {
     
	title:''
}
export default state
  1. 在main.js中引入,且挂在到vue属性上
import store from './store'; // 或 import store from './store/index';
Vue.prototype.$store = store;
  1. 具体组件中使用:
/* 设置相关值 /*
(推荐)this.$store.commit('setTitle','new title') 
或 this.$store.state.title = 'new title'

/* 获取相关值 */
let title = this.$store.state.title
console.log(title)

2.2.2 $bus

之前提到过$bus可以用来替代父子组件间的$emit传值,其实,这样的表述并不准确,应该说在兄弟组件之间,没有像父子组件那样直接建立监听的情况下,$bus用来辅助$emit来建立一个事件监听的机制,以达到兄弟组件间传值的效果。
下面简单介绍一下$bus的使用,建立全局的$bus监听机制。
在src目录下,新建bus目录,新建vue-bus.js文件

  1. vue-bus.js
const install = (Vue) =>{
     
  const Bus = new Vue({
     
    methods : {
     
      emit(event,...args){
     
        this.$emit(event,...args)
      },
      on(event,callback){
     
        this.$on(event,callback)
      },
      off(event,callback){
     
        this.$off(event,callback)
      },
    }
  })
  Vue.prototype.$bus = Bus
}
export default install
  1. 在main.js中引入
import VueBus from './bus/vue-bus'
Vue.use(VueBus)
  1. 在具体组件中使用
    1)在A组件构建的声明周期created或mounted中创建bus监听:
created(){
     
 this.$bus.on('updateVal',(val)=>{
       // updateVal是事件监听的名称,callBack采用了ES6的function的写法
    this.updateTitle(val.title)     // updateTitle是组件A中的方法
 })
},
beforeDestroy() {
     
   //清除bus的事件监听,不加参数则表示清除所有
   this.$bus.off('sendresponse')
}

2)在B组件中调用该方法

methods:{
     
	updateTitle(){
     
		this.$bus.$emit("updateVal",this.title)
	}
}

2.3 组件间通信注意事项

  1. 相较于vuex,属性集中写在单个文件中而言,bus监听直接写在某个具体的组件中,因此,如果过多的使用,会造成一定的维护困难。
  2. $bus$emit,都是对事件(event)的监听,因此,在使用时,要和methods中的方法(function)区分开。如下所示:
@click = "updateTitle" 
=>  @event_name(事件名称) = 'function_name'(方法名称)
  1. 如果在组件中对vuex中声明的属性进行监听(watch),那么也可以达到$bus的效果。如下所示:
/* 1. 在vuex中声明属性 title*/
const state = {
     
	title:''
}
export default state
/* 2. 在具体组件A中对其进行监听 */
watch:{
     
'$store.state.title':{
     
    handler(newVal, oldVal) {
     
      	this.updateTitle(newVal)
     },
     immediate: true, //该回调将会在侦听开始之后被立即调用(值被绑定时就发生监听回调,而非只有后期改变的时候才发生变化)
     deep:true       //该回调会在任何被侦听的对象的 property 改变时被调用,不论其被嵌套多深(深度对象需要)
   },
}
/* 3.当有其他组件修改了vuex中title值的时候,写了对应侦听器的组件就会发生响应 */B组件中调用
this.$store.commit('setTitle','new title')

三. axios 服务请求

在vue中,通常使用axios来替代jQuery中的ajax,进行服务端数据的请求。

3.1 基本请求配置

下面将给出一个具体的配置方法:

  1. 在src目录下,新建request目录,新建http.js文件
/* 1. http.js文件(./src/request/http.js)*/
import axios from 'axios';
import QS from 'qs';
import router from '../router'
/* 配置请求基本信息 */
axios.defaults.timeout = 20000;   // 超时时间
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8';  //请求头类型,form表单提交
/* 配置不同的地址环境,下面根据不同的项目来说明 */
const devUrl = {
     
	"item-1":"http://dev_ip:port/item_name_1",
	"item-2":"http://dev_ip:port/item_name_2".
	...
	"item-n":"http://dev_ip:port/item_name_n"
}
const testUrl = {
     
 	"item-1":"http://test_ip:port/item_name_1",
	"item-2":"http://test_ip:port/item_name_2".
	...
	"item-n":"http://test_ip:port/item_name_n"
}
const prodUrl = {
     
 	"item-1":"http://prod_ip:port/item_name_1",
	"item-2":"http://prod_ip:port/item_name_2".
	...
	"item-n":"http://prod_ip:port/item_name_n"
}
const baseUrl = {
     
  "development":devUrl,
  "testing":testUrl,
  "production":prodUrl
}
/* 从build中获取到构建的环境类型,进而获取到确切的地址配置 */
/* process.env.NODE_ENV 来自于 build(./src/build/build.js)中的配置信息。*/
const url = baseUrl[process.env.NODE_ENV]
/* 配置请求拦截器 */
axios.interceptors.request.use(
  config => {
     
    // 每次发送请求之前判断是否存在token
    // 如果存在,则统一在http请求的header都加上token,这样后台根据token判断你的登录情况
    // 即使本地存在token,也有可能token是过期的,所以在响应拦截器中要对返回状态进行判断
    let token = localStorage.getItem("token");
    for(let item in url){
     
      if (config.url.includes(item)) {
     
        let realUrl = config.url.replace(item,"")
        config.url = url[item] + realUrl;/*拼接完整请求路径*/
        break
      }
    }
    token && (config.headers.token = token);
    return config;
  },
  error => {
     
    return Promise.error(error);
  })
/* 配置响应拦截器,具体项目,具体定制*/
axios.interceptors.response.use(
  response => {
     
    // 如果返回的状态码为200,说明接口请求成功,可以正常拿到数据
    // 否则的话抛出错误
    if(response.data.flag==='-99'){
      // -99 即后台权限验证失败的返回值,则返回到登陆页面
      localStorage.removeItem('token')
      router.replace({
     path: '/login'});
      return
    }
    if (response.status === 200) {
     
      return Promise.resolve(response);
    } else {
     
      return Promise.reject(response);
    }
  },
  // 服务器状态码不是2开头的的情况
  // 这里可以跟你们的后台开发人员协商好统一的错误状态码
  // 然后根据返回的状态码进行一些操作,例如登录过期提示,错误提示等等
  // 下面列举几个常见的操作,其他需求可自行扩展
  error => {
     
    if (error.response.status) {
     
      switch (error.response.status) {
     
        // 401: 未登录
        // 未登录则跳转登录页面,并携带当前页面的路径
        // 在登录成功后返回当前页面,这一步需要在登录页操作。
        case 401:
          router.replace({
     
            path: '/login',
            query: {
     
              redirect: router.currentRoute.fullPath
            }
          });
          break;

        // 403 token过期
        // 登录过期对用户进行提示
        // 清除本地token和清空vuex中token对象
        // 跳转登录页面
        case 403:
          Toast({
     
            message: '登录过期,请重新登录',
            duration: 1000,
            forbidClick: true
          });
          // 清除token
          localStorage.removeItem('token');
          store.commit('loginSuccess', null);
          // 跳转登录页面,并将要浏览的页面fullPath传过去,登录成功后跳转需要访问的页面
          setTimeout(() => {
     
            router.replace({
     
              path: '/login',
              query: {
     
                redirect: router.currentRoute.fullPath
              }
            });
          }, 1000);
          break;

        // 404请求不存在
        case 404:
          Toast({
     
            message: '网络请求不存在',
            duration: 1500,
            forbidClick: true
          });
          break;
        // 其他错误,直接抛出错误提示
        default:
          Toast({
     
            message: error.response.data.message,
            duration: 1500,
            forbidClick: true
          });
      }
      return Promise.reject(error.response);
    }
  })
/* 配置get方法,可以直接使用this.get(url,param) 进行调用*/
export function get(url, params) {
     
	return new Promise((resolve, reject) => {
     
	  axios.get(url, {
     
	    params: params
	  }).then(res => {
     
	    resolve(res.data);
	  }).catch(err => {
     
	    reject(err.data)
	  })
	});
}
/* 配置post方法(form表单提交数据方式),可以直接使用this.post(url,param) 进行调用*/
export function post(url, params) {
     
	return new Promise((resolve, reject) => {
     
	  axios.post(url, QS.stringify(params, {
     indices: false}))
	    .then(res => {
     
	      resolve(res.data);
	    })
	    .catch(err => {
     
	      reject(err.data)
	    })
	});
}
/* 配置postJson方法(json数据提交方式),替换配置请求头类型,可以直接使用this.postJon(url,param) 进行调用*/
export function postJson(url, params) {
     
	return new Promise((resolve, reject) => {
     
	 axios.post(url, JSON.stringify(params),{
     
	   headers:{
     
	     'Content-Type': 'application/json'
	   }
	 }).then(res => {
     
	     resolve(res.data);
	 }).catch(err => {
     
	     reject(err.data)
	   })
	});
}
/* 配置postFormData方法(文件file数据提交方式),替换配置请求头类型,可以直接使用this.postFormData(url,param) 进行调用*/
export function postFormData(url,formData){
     
  return new Promise((resolve, reject) => {
     
    axios.post(url, formData,{
     
      headers:{
     
        'Content-Type' :'multipart/form-data'
      }
    }).then(res => {
     
        resolve(res.data);
    }).catch(err => {
     
        reject(err.data)
      })
  });
}
  1. 在main的js中引入文件
import {
     get,post,postJson,postFormData} from "@/request/http";
Vue.prototype.post = post;
Vue.prototype.get = get;
Vue.prototype.postJson = postJson
Vue.prototype.postFormData = postFormData
Vue.prototype.fetch = fetch;
  1. 具体方法中使用
this.post('item_1/请求地址',params)
	.then((resp)=>{
     
	
	}).error((err)=>{
     
	
	})

3.2 通用型配置示例

如果有些请求方法,只需要进行数据提交,然后打印响应信息,而不需要对数据进行具体处理,也可以配置通用的接口。

  1. 在http.js同目录下新建 common文件
/* common.js (./js/request/common.js) */
import {
     get, post,postJson} from './http'
import {
     Message} from 'element-ui';

//基本请求只提取消息并展示
export const postAndSendMsg = (url, params) => {
     
  return  new Promise((resolve, reject) => {
     
    post(url, params).then(res => {
     
      if(res.flag==1){
     
        Message.success({
     
          message: res.msg
        });
      }else{
     
        Message.error({
     
          message: res.msg
        });
      }
      resolve(res)
    }).catch(err => {
     
      console.log(err)
      Message.error({
     
        message: '操作异常'
      });
      resolve(err)
    })
  })
}
//基本请求只提取消息并展示
export const postJsonAndSendMsg= (url, params) => {
     
  return new Promise((resolve, reject) => {
     
    postJson(url, params).then(res => {
     
      if (res.flag == 1) {
     
        Message.success({
     
          message: res.msg
        });
      } else {
     
        Message.error({
     
          message: res.msg
        });
      }
      resolve(res)
    }).catch(err => {
     
      console.log(err)
      Message.error({
     
        message: '操作异常'
      });
      resolve(err)
    })
  })
}
  1. 在main.js中引入
import * as commonApi from '@/request/common'
Vue.prototype.commonApi = commonApi 
  1. 在具体组件中调用
this.commonApi.postAndSendMsg(url,params)

四、vue路由router

4.1. 路由跳转方式

参考官方网站,很好理解。

A -> B -> C
//在B页面跳转至C页面
this.$router.replace(path) // 从当前页B跳转至path路径页C,且当前B页面不会加到页面的历史记录上,在C页面点击浏览器后退按钮后,不会退回到B页,还是退回到A页;
this.$router.push(path)  // 从当前页B跳转至path路径页C,且当前B页不会加到页面的历史记录上,在C页面点击浏览器后退按钮后,会退回到B页

4.2 动态路由匹配

4.2.1 动态路由匹配及路由传参

  1. 不同事物(对象)可以使用同一组件(页面)进行数据渲染时,可以使用动态路由的方式。
    例如,我们有一个 User 组件,对于所有 ID 各不相同的用户,都要使用这个组件来渲染。因为该事物(对象)只是属性值不同,而非属性不同,且页面展示效果是一致的。
    router的使用方式如下:
    在定义router实例时,要对路由的路径进行声明。
    // 1. 在router/index.js文件中声明
    const router = new VueRouter({
           
      routes: [
        // 动态路径参数 以冒号开头
        {
            path: '/user/:id', component: User }
      ]
    })
    
    // 2. 在其他组件中调用
    let id = "123"
    this.$router.push("/user/"+id)
    
    // 3. 在User组件中获取参数id
    let id = this.$route.params.id
    

通常情况下,如上操作,我们就完成了,从一个列表页面传递参数id值,进入一个对象详情的渲染页,在页面构造之后,在user组件的生命周期中通过这个id来从后端请求数据。
但是,在官方文档中提到下面一句话,提到了组件复用会导致的问题。

提醒一下,当使用路由参数时,例如从 /user/foo 导航到
/user/bar,原来的组件实例会被复用。因为两个路由都渲染同个组件,比起销毁再创建,复用则显得更加高效。不过,这也意味着组件的生命周期钩子不会再被调用。

演示示例:

从演示中可以看到,生命周期中从路由获取的id只取到了一次,在监听路有变化时,可以接收到最新的参数值。

  1. 捕获所有路由或 404 Not found 路由
    详情参见官方文档。
    通常,不同的企业会适配自己的404 not found页面,让其尽量少的显示其他信息且尽量将页面设计的醒目。这时,就可以用到通配符路由 { path: '*' },例如在路由配置中,可以配置自己设计的404页面。
{
     
	name:'404',
    path:'*',
    component:() => import('@/components/pages/404.vue'),
}

因为路由匹配是从上往下按照路由配置的顺序来进行的,所以通配符路由尽量写在准确匹配路由的下面,相当于是做最后一道拦截。

官方文档中的说法:匹配的优先级就按照路由的定义顺序:谁先定义的,谁的优先级就最高。

vue必知必会,Vue在工程项目中常用知识点梳理与总结。_第2张图片
同样地,如果我们的系统是经典的上左右布局,我们还可以在子路由中配置404的错误页面,这样就可以只在需要的路径下显示404页面,如下图所示。
vue必知必会,Vue在工程项目中常用知识点梳理与总结。_第3张图片

4.3 路由组件传参

在上面4.1节提到过,通过路由传参,我们就可以在跳转后的页面取到我们需要传递的对象。但是在vue官网中提到了一种使用props属性来接收路由参数的方式,意在将路由和组件解耦,路由组件传参非常建议使用这种方式。
传递单个参数时的使用方式如下:

// 1. 在声明路由组件时,要注明组件是使用props接收参数的
const router = new VueRouter({
     
  routes: [
    {
      
     name:'user'
     path: '/user/:id',
     component: User, 
     props: true 
     },
    // 对于包含命名视图的路由,你必须分别为每个命名视图添加 `props` 选项:
    {
     
      path: '/user/:id',
      components: {
      default: User, sidebar: Sidebar },
      props: {
      default: true, sidebar: false }
    }
  ]
})
// 2. 在跳转后的页面user组件中声明props属性中对应的值
<template>
</template>
<script>
export default {
     
	name:'',
	props:['id'] 
	// 或者
	props:{
     
		id:String
	}
	// 或者
	props:{
     
		id:{
     
			trype:String,
			required: true
		}
	}
}
</script>
// 3.在需要跳转的页面使用路由跳转,在编程式路由中主要有以下方式:
(1) 
(1.1)传递对象:
组件名称name 和 参数params组合 (在配置路由表时,需配置对应的name。浏览器地址栏中看不到传递参数)
传值页面Athis.$router.push({
      name: "testReceiveParams", params: {
      val: this.val } });
接收参数页面B<template>
  <div>
    接收到的参数值为:{
     {
      val }}
    <el-button @click="toNextPage">下一页</el-button>
  </div>
</template>
 data() {
     
    return {
     
      val: "", //声明组件中的值,以便能够留存
    };
  },
   created() {
     
  //第一次传参进入组件时,会调起声明周期钩子函数给组件赋值,但后续再次进入时,由于组件复用,声明周期钩子则不再调用,本方法只会执行一次。故生命周期中只能用作首次赋值。
    this.val = this.$route.params.val;
  },
  
  watch: {
     
    $route(to, from) {
     
      console.log(to, from);
      // 对路由变化作出响应(首次不响应,赋值在声明周期钩子中),路由中的params参数是会即时更新的,当判断进入路由的参数中能够取得到值时(即下面代码的if判断语句),再更新值;否则,如果从当前页B再跳入下一页C后,再次从C页回退到B页,从C到B的这次路由跳转中将不会存在携带B中需要的值,监听路由时会被覆盖为空字符串。
      if (to.params.val !== undefined) {
     
        this.val = to.params.val;
      }
    },
  },
  (1.2)传普通类型
 组件路径path 和 参数params组合 (在配置路由表时,在path上配置参数名称path: "/test/testReceiveParams/:val",。浏览器地址栏中能看到传递参数)
传值页面Athis.$router.push({
      path: `/test/testReceiveParams/${
       param}` });
接收参数页面B:同(1.1),该方式接收参数,也是在路由的params参数中。
(2)路径path和和参数query组合(无需路由配置,浏览器地址栏中能看到参数)
(2.1)传值页面A(传普通类型):this.$router.push({
      path: "/test/testReceiveQuery", query: {
      val:  this.val } });
接收参数页面B<template>
  <div>
    接收到的参数值为:{
     {
      val }}
    <el-button @click="toNextPage">下一页</el-button>
  </div>
</template>
computed: {
     
	//如果接收的是普通类型参数,则使用计算属性即可满足要求,当参与计算属性计算过程的值是双向绑定时,计算属性的值也会即时更新。且从C页面回退至B本页面时,浏览器地址栏中会保留参数,因此可以即时获取之前从A传进来的参数值。
   val: function () {
     
     return this.$route.query.val;
   },
 },2.2)传值页面A(传对象类型):this.$router.push({
      path: "/test/testReceiveQuery", query: {
      val: {
      id: this.val } } });
 接收参数页面B<template>
  <div>
    接收到的参数值为:{
     {
      val }}
    <el-button @click="toNextPage">下一页</el-button>
  </div>
</template>
  created() {
     
  // 1.使用此方式接收参数时,首次进入组件页面,不会触发路由监听的功能,首次进入需要以此方式进行赋值。
    this.val = this.$route.query.val;
  },
  watch: {
     
    $route(to, from) {
     
      //2.再次进入时,会触发路由监听功能,进行赋值。但多了一个类型判断的条件,因为从若是从C页面返回到B页面时,此次路由中的query携带的是"[Object object]"值,无法被正确解析,故为避免被覆盖,则多加一个类型判断。
      console.log(to, from, typeof to.query.val);
      // 对路由变化作出响应...
      if (to.query.val !== undefined && to.query.val instanceof Object) {
     
        this.val = to.query.val;
      }
    },
  },
  (3)路由名称name 和 params,配合组件props属性。(需要在路由中配置props:true)
注:使用组件的props接收参数,就没有使用this.$route.params获取参数那样组件复用的担心,使用props接收可以保证每次都是最新值。
但依然存在和仅使用params传值一样的问题,即从下一个页面C页返回时,props中的值会不存在了,即C组件并没有向B组件传值,因此仍需要使用监听来完成,示例如下。
传参组件Athis.$router.push({
      name: "testReceiveProps", params: {
      val: {
      id: this.val } } });
接收组件B<template>
  <div>
    接收到的参数值为:{
     {
      currentVal }}
    <el-button @click="toNextPage">下一页</el-button>
  </div>
</template>
<script>
export default {
     
  name: "receiveParams",
  props: ["val"],
  data() {
     
    return {
     
      currentVal: "", //1.记录当前页面值,防止被覆盖
    };
  },
  created() {
     
    // this.newVal = this.val;   //2.生命周期钩子中的赋值和watch监听中的immediate: true可以二选其一,目的在于保证首次进入时,能够获取到传入的值。
  },
  watch: {
     
    val: {
     
      handler: function (oldVal) {
     
        //3.对值进行判断,否则从C页退回B页时,值会被覆盖
        if (oldVal !== undefined) {
       
          this.currentVal = oldVal;
        }
      },
      immediate: true, //2.生命周期钩子中的赋值和watch监听中的immediate: true可以二选其一,目的在于保证首次进入时,能够立即启用监听,获取到传入的值。
    },
  },
  methods: {
     
    toNextPage() {
     
      this.$router.push("/test/testReceiveToNextPage");
    },
  },
};
</script>

五、Vue工程项目相关配置

1.开启控制台显示局域网访问地址
背景:
vue版本 2.5.2
vue脚手架版本:2.9.6(vue -V查看)
使用vue init webpack命令构建vue工程后,运行时,在控制台只显示了本地访问地址,没有局域网的network地址。
下面记录下一种可行的方案:

  1. 修改config/index.js中的host
    vue必知必会,Vue在工程项目中常用知识点梳理与总结。_第4张图片
    2.修改build/webpack.dev.conf.js中的messages值
messages: [
	`Your application is running here:`,
	`Local:http://localhost:${
       port}`,
	`NetWork:http://${
       require('ip').address()}:${
       port}`
]

vue必知必会,Vue在工程项目中常用知识点梳理与总结。_第5张图片
3. 如果想修改启动的服务标识,可以修改package.json中的相关配置
vue必知必会,Vue在工程项目中常用知识点梳理与总结。_第6张图片
4.效果
使用npm run serve命令后,控制台的显示
vue必知必会,Vue在工程项目中常用知识点梳理与总结。_第7张图片

未完待续。。。

你可能感兴趣的:(前端,vue,js,前端)