数据双向绑定给我们开发带来了方便,在vue官方文档中单独用了一节来介绍响应式原理,需要深入了解的可以移步官网。下面仅梳理一下常用的数据变化处理方式。
注:Vue不允许动态添加根级响应式,因此响应式的对象处理,最好是在data中进行声明,并赋予初始值或空值(’’)。
<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,使新旧列名对象混合来达到动态表格的效果。
<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)
*/
数组的变异和非变异方法,参考官方网站:数组更新检测
为了在数据变化之后等待 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) // => '已更新'
}
}
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>
组件关系解释:
使用技能:prop
、$emit
和$refs
父子关系:
组件A <- 组件A_child1
组件A <- 组件A_child2
子组件可以使用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>
流程示意图如下:
注意: 在 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中只显示了父组件第一次传过来的值。
$refs
和$emit
)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")
*/
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的方法。
使用技能:vuex 和 $bus
兄弟关系:
组件A <=> 组件B
组件A_child1<=> 组件A_child2
同prop
属性一样,vuex
可以用来替换prop传值。
同$emit
一样,$bus
总线可以用来组件对事件的监听
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
)。
/* 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
})
/* mutations.js */
const mutations = {
// 设置侧边栏菜单是否收起isMenuClose的值
setTitle(state, data) {
state.title = data;
},
}
export default mutations
/* state.js */
const state = {
title:''
}
export default state
import store from './store'; // 或 import store from './store/index';
Vue.prototype.$store = store;
/* 设置相关值 /*
(推荐)this.$store.commit('setTitle','new title')
或 this.$store.state.title = 'new title'
/* 获取相关值 */
let title = this.$store.state.title
console.log(title)
之前提到过$bus
可以用来替代父子组件间的$emit
传值,其实,这样的表述并不准确,应该说在兄弟组件之间,没有像父子组件那样直接建立监听的情况下,$bus
用来辅助$emit
来建立一个事件监听的机制,以达到兄弟组件间传值的效果。
下面简单介绍一下$bus
的使用,建立全局的$bus
监听机制。
在src目录下,新建bus目录,新建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
import VueBus from './bus/vue-bus'
Vue.use(VueBus)
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)
}
}
vuex
,属性集中写在单个文件中而言,bus监听直接写在某个具体的组件中,因此,如果过多的使用,会造成一定的维护困难。$bus
和$emit
,都是对事件(event)
的监听,因此,在使用时,要和methods
中的方法(function)
区分开。如下所示:@click = "updateTitle"
=> @event_name(事件名称) = 'function_name'(方法名称)
$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')
在vue中,通常使用axios来替代jQuery中的ajax,进行服务端数据的请求。
下面将给出一个具体的配置方法:
/* 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)
})
});
}
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;
this.post('item_1/请求地址',params)
.then((resp)=>{
}).error((err)=>{
})
如果有些请求方法,只需要进行数据提交,然后打印响应信息,而不需要对数据进行具体处理,也可以配置通用的接口。
/* 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)
})
})
}
import * as commonApi from '@/request/common'
Vue.prototype.commonApi = commonApi
this.commonApi.postAndSendMsg(url,params)
参考官方网站,很好理解。
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页
// 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只取到了一次,在监听路有变化时,可以接收到最新的参数值。
{ path: '*' }
,例如在路由配置中,可以配置自己设计的404页面。{
name:'404',
path:'*',
component:() => import('@/components/pages/404.vue'),
}
因为路由匹配是从上往下按照路由配置的顺序来进行的,所以通配符路由尽量写在准确匹配路由的下面,相当于是做最后一道拦截。
官方文档中的说法:匹配的优先级就按照路由的定义顺序:谁先定义的,谁的优先级就最高。
同样地,如果我们的系统是经典的上左右布局,我们还可以在子路由中配置404的错误页面,这样就可以只在需要的路径下显示404页面,如下图所示。
在上面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。浏览器地址栏中看不到传递参数)
传值页面A:this.$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",。浏览器地址栏中能看到传递参数)
传值页面A:this.$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组件传值,因此仍需要使用监听来完成,示例如下。
传参组件A:this.$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>
1.开启控制台显示局域网访问地址
背景:
vue版本 2.5.2
vue脚手架版本:2.9.6(vue -V
查看)
使用vue init webpack
命令构建vue工程后,运行时,在控制台只显示了本地访问地址,没有局域网的network地址。
下面记录下一种可行的方案:
messages: [
`Your application is running here:`,
`Local:http://localhost:${
port}`,
`NetWork:http://${
require('ip').address()}:${
port}`
]
3. 如果想修改启动的服务标识,可以修改package.json中的相关配置
4.效果
使用npm run serve
命令后,控制台的显示
未完待续。。。