vue相关技术总结

本文深入探讨Vue.js中的核心概念,包括@vue/cli-service的使用,Vue.component与Vue.extend的区别,以及Vue实例的$el、$data、$options和$refs属性。详细解析了Vue中$refs的用法,展示了如何利用v-model在组件间双向绑定数据。此外,文章还涵盖了Vue的初始化el、组件上的v-model、$destroy方法以及如何手写这两个方法。接着,讨论了子组件如何修改props、处理props与data响应式的问题,以及使用sync和v-model实现父子组件的双向通信。最后,文章深入介绍了Vue中的异步组件、动态组件、keep-alive的使用,以及$nextTick的理解,帮助读者全面掌握Vue.js开发中的关键知识点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

)@vue/cli-service

"serve":"vue-cli-service serve","build":"vue-cli-service build","lint":"vue-cli-service lint"

那么vue-cli-serve 执行了怎样的命令,怎么实现这些命令的呢?我们需要知道vue-cli-service是什么。

vue-cli官方文档中我们知道在一个 Vue CLI 项目中,@vue/cli-service 安装了一个名为 vue-cli-service 的命令。你可以在 npm scripts 中以 vue-cli-service、或者从终端中以 ./node_modules/.bin/vue-cli-service 访问这个命令。vue-cli-service 其实执行的是 node_modules/.bin/vue-cli-service

最终是执行了@vue/cli-service

除了通过命令行,可以使用vue.config.js中devServer字段配置开发服务器的参数;下面给出一段配置的例子:

2)详解Vue.component和Vue.extend

Vue.extend({})简述:使用vue.extend返回一个子类构造函数,也就是预设部分选项的vue实例构造器。

后可使用vue.component进行实例化、或使用new extendName().$mount(''+el)方式进行实例化(从而实现模拟组件)。

vue中$el等属性

属性:

vm.$el

获取Vue实例关联的DOM元素;

vm.$data

获取Vue实例的data选项(对象)

vm.$options

获取Vue实例的自定义属性(如vm.$options.methods,获取Vue实例的自定义属性methods)

vm.$refs

获取页面中所有含有ref属性的DOM元素(如vm.$refs.hello,获取页面中含有属性ref = “hello”的DOM元素,如果有多个元素,那么只返回最后一个)

Js代码

var app = new Vue({ el:"#container", data:{ msg:"hello,2018!" }, address:"长安西路" })

console.log(app.$el);

返回Vue实例的关联DOM元素,在这里是#container

console.log(app.$data);

返回Vue实例的数据对象data,在这里就是对象{msg:”hello,2018“}

console.log(app.$options.address);

返回Vue实例的自定义属性address,在这里是自定义属性address

console.log(app.$refs.hello)

返回含有属性ref = hello的DOM元素(如果多个元素都含有这样的属性,只返回最后一个)<h3 ref = "hello">呵呵 1{{msg}}</h3>

VUE的$refs和$el的使用

ref有三种用法:

1、ref 加在普通的元素上,用this.$refs.(ref值) 获取到的是dom元素

3、如何利用 v-for 和 ref 获取一组数组或者dom 节点

如果通过v-for 遍历想加不同的ref时记得加 :号,即 :ref =某变量 ;

  

这点和其他属性一样,如果是固定值就不需要加 :号,如果是变量记得加 :号。(加冒号的,说明后面的是一个变量或者表达式;没加冒号的后面就是对应的字符串常量(String)

应注意的坑有:

1、ref 需要在dom渲染完成后才会有,在使用的时候确保dom已经渲染完成。比如在生命周期 mounted(){} 钩子中调用,或者在 this.$nextTick(()=>{}) 中调用。

2、如果ref 是循环出来的,有多个重名,那么ref的值会是一个数组 ,此时要拿到单个的ref 只需要循环就可以了。

vm.$el

获取Vue实例关联的DOM元素;

比方说我这里想获取自定义组件tabControl,并获取它的OffsetTop。就需要先获取该组件。

在组件内设置 属性 ref=‘一个名称(tabControl2)’,

然后 this.$refs.tabControl2 就拿到了该组件

切记:ref属性,而获取组件的时候要用$refs

获取 OffsetTop,组件不是DOM元素,是没有OffsetTop的,无法通过 .OffsetTop来获取的。就需要通过$el来获取组件中的DOM元素

3)Vue.js之初始化el

Vue中的el,称之为挂载点,它有两种写法

1.new Vue({ el: '#app' });

2.new Vue({ }).$mount('#app');

  • Vue会管理与Vue进行el绑定的元素及其内部的后代元素,建议使用id选择器,可以使用其他的双标签,不能使用html和body。
  • el绑定的元素内,都是Vue的作用范围。
  • 一个Vue实例只能绑定一个el。

4)组件上的v-model

在子组件中声明 emits 自定义事件,格式为 update:xxx

调用 $emit() 触发自定义事件,更新父组件中的数据

  • 自定义事件可以用于开发支持 v-model 的自定义表单组件。回忆一下 v-model 在原生元素上的用法:<input v-model="searchText" />

上面的代码其实等价于下面这段 (编译器会对 v-model 进行展开):

<input

:value="searchText"

@input="searchText = $event.target.value"

/>

而当使用在一个组件上时,v-model 会被展开为如下的形式:

<CustomInput

:modelValue="searchText"

@update:modelValue="newValue => searchText = newValue"

/>

要让这个例子实际工作起来,<CustomInput> 组件内部需要做两件事:

  1. 将内部原生 input 元素的 value attribute 绑定到 modelValue prop
  2. 输入新的值时在 input 元素上触发 update:modelValue 事件

这里是相应的代码:

<!-- CustomInput.vue -->

<script>

export default {

props: ['modelValue'],

emits: ['update:modelValue']

}

</script>

<template>

<input

:value="modelValue"

@input="$emit('update:modelValue', $event.target.value)"

/>

</template>

现在 v-model 也可以在这个组件上正常工作了

<CustomInput v-model="searchText" />

另一种在组件内实现 v-model 的方式是使用一个可写的,同时具有 getter 和 setter 的计算属性。get 方法需返回 modelValue prop,而 set 方法需触发相应的事件:

<!-- CustomInput.vue -->

<script>

export default {

props: ['modelValue'],

emits: ['update:modelValue'],

computed: {

value: {

get() {

return this.modelValue

},

set(value) {

this.$emit('update:modelValue', value)

}

}

}

}

</script>

<template>

<input v-model="value" />

</template>

v-model 的参数#

默认情况下,v-model 在组件上都是使用 modelValue 作为 prop,并以 update:modelValue 作为对应的事件。我们可以通过给 v-model 指定一个参数来更改这些名字:<MyComponent v-model:title="bookTitle" />

在这个例子中,子组件应声明一个 title prop,并通过触发 update:title 事件更新父组件值:

<!-- MyComponent.vue -->

<script>

export default {

props: ['title'],

emits: ['update:title']

}

</script>

<template>

<input

type="text"

:value="title"

@input="$emit('update:title', $event.target.value)"

/>

</template>

多个 v-model 绑定#

利用刚才在 v-model参数小节中学到的技巧,我们可以在一个组件上创建多个 v-model 双向绑定,每一个 v-model 都会同步不同的 prop:

<UserName

v-model:first-name="first"

v-model:last-name="last"

/>

<script>

export default {

props: {

firstName: String,

lastName: String

},

emits: ['update:firstName', 'update:lastName']

}

</script>

<template>

<input

type="text"

:value="firstName"

@input="$emit('update:firstName', $event.target.value)"

/>

<input

type="text"

:value="lastName"

@input="$emit('update:lastName', $event.target.value)"

/>

</template>

5)自定义组件的 v-model

一个组件上的 v-model 默认会利用名为 value 的 prop 和名为 input 的事件,但是像单选框、复选框等类型的输入控件可能会将 value 特性用于不同的目的。model 选项可以用来避免这样的冲突:

告知vue底层该组件监听checked属性的change事件。

这里的 lovingVue 的值将会传入这个名为 checked 的 prop。同时当 <base-checkbox> 触发一个 change 事件并附带一个新的值的时候,这个 lovingVue 的属性将会被更新。

注意你仍然需要在组件的 props 选项里声明 checked 这个 prop。

从官网上看到,v-model在内部为不同的输入元素使用不同的属性并抛出不同的事件:

text和textarea元素使用value属性和input事件

checkbox和radio使用checked属性和change事件

select使用value和change事件

因为自定的组件并没有默认的value和input事件,在使用时,我们需要按照上面那样显式的去声明定义这些东西。这时,需要model选项,在定义组件的时候,指定prop的值和监听的事件。

6)vm.$destroy()

完全销毁一个实例。清理与其他实例的连接,解绑它的全部指令及事件监听器。
触发beforeDestroy和destroyed的钩子

这是官方讲解,在实际操作时发现,通过这个方法销毁实例 ,dom是仍然存在的,并且data和methods中的数据也在,在 destroyed钩子中都可以拿到,甚至你绑定的点击事件也仍然可以有效触发。

那么,它到底销毁了什么?
执行vm.$destroy()之后,watcher失效,无法通过数据改变视图

所以在大多数场景下你不应该调用这个方法,官方推荐使用v-if或v-for指令以数据驱动试图的方式控制子组件的生命周期。

它并不是用来清除已有页面上的DOM的。

包括Watcher对象从其所在Dep中释放

手写$forceUpdate,vm.$destroy方法

学习vue源码(15)手写$forceUpdate,vm.$destroy方法_vm.$destroy();-优快云博客

7)子组件修改props:

Vue.js之props组件传值 - 掘金

当通过props传递过来的是值是引用类型的时候,如数组或者对象,此时我们通过子组件更改其传递过来的值,也会改变父组件的值,但是是不会像基本类型那样控制台报警告的,这是需要注意的

由于在js中,对象和数组是引用类型,指向同一个内存空间,所以当我们通过v-bind绑定的参数是一个数组或对象,然后传递过来时,那么他们在子组件的改变会影响到父组件的,(这和vue组件无关,由js特性所决定的,如果想避免这种情况,那么在子组件接收到此引用类型参数后处理下即可,如用JSON方法处理或用Object.assign()方法处理等)

此时使用data声明的变量由于值中转了下,然后也不是引用类型,所以在这里我们父组件对此参数修改了,子组件就不会同步更改,如果此时想同步更改,我们可以是使用

watch: 可使用sync模拟v-model语法糖,实现父子组件传值双向绑定

8)Vue:父子组件传值props、sync、v-model详解

正常父子传值:

如果想prop(num)进行“双向绑定”。 因此推荐以 update:myPropName的模式触发事件取而代之。.sync修饰符

v-model和.sync修饰符的区别:

两者本质都是一样,并没有任何区别: “监听一个触发事件”="(val) => value = val"。

.sync与v-model区别是

相同点:都是语法糖,都可以实现父子组件中的数据的双向通信。

区别点:

格式不同。 v-model="num", :num.sync="num"

v-model: @input + value

:num.sync: @update:num

v-model只能用一次;.sync可以有多个。

只不过v-model默认对应的是input或者textarea等组件的input事件,如果在子组件替换这个input事件,其本质和.sync修饰符一模一样。比较单一,不能有多个。// 子组件可以用自定义事件,来替换v-model默认对应的原生input事件,只不过我们需要在子组件手动 $emit

model: {

prop: "value",

event: "update"

},

一个组件可以多个属性用.sync修饰符,可以同时"双向绑定多个“prop”,而并不像v-model那样,一个组件只能有一个。

因为使用v-model,子组件中只能触发一个input事件,事件名是唯一的,而.sync修饰符,它触发的时间名是updata:属性名,所以它有多个事件名,就可以使用多次

props/$emit是父子组件最常用的通信方式,而v-model、.sync只是其语法糖

子组件只是单一的修改某个父组件值的话,表单类组件使用v-model语法糖

子组件只是单一的修改某个父组件值的话,非表单类组件使用sync语法糖

复杂逻辑还是老老实实props/$emit

其实语法糖只是在父组件用的时候更加方便,而子组件该咋样还是咋样。

9)vue中的异步组件

我们在讲异步组件之前,我们再来回顾一下webpack打包时的分包操作。我们可以使用import()异步加载模块来实现分包操作。import函数的返回值是一个Promise,所以我们可以使用then进行下一步处理。

如下图所示为打包后的文件目录,因为我们如果同步加载math.js文件,此时就不存在中间的文件,此时当浏览器请求资源时,就会很慢。

一、vue中的异步组件

通过上面的webpack配置我们明白了为什么要进行分包操作,此时我们想一个问题,如果一个网站的页面在用户第一次浏览器时就将全部页面都下载了,这样会出现一个问题,就是首屏加载过慢。

如果我们的项目过大了,对于某些组件我们想要异步加载(也就是分包处理),此时Vue给我们提供了一个函数defineAsyncComponentdefineAsyncComponent可以传入两种类型的参数,第一个是函数,该函数需要返回Promise,第二个参数是一个对象类型,对异步函数进行配置。

第一种写法:函数写法

打包后的文件

第二种写法:对象写法

如图所示是可以实现分包操作,相面详细介绍一下传入对象中的选项。

loader选项:需要一部加载的模块,对应的是一个函数。

loadingComponent:加载过程中显示的组件。

errorComponent:加载失败时显示的组件。

delay:给出时间,当加载时间超过该时间,直接显示error组件。

suspensible:定义组件是否可挂起,默认是true。

一、异步组件的简介

所谓的异步组件就是通过import或者require导入的vue组件。

例如:const componentA = import(’@/components/componentA.vue’);

或者 const componentA = require(’@/components/componentA.vue’);

二、使用异步组件的好处

可以避免页面一加载时就去加载全部的组件,从而导致页面访问时间变长的问题。使用异步加载组件后,只有当需要某个组件时才会去加载需要的组件。

三、定义异步组件

1、setTimeout

var resCom = { template: "#async-example" }

Vue.component('async-example', function(resolve, reject){

setTimeout(function(){

// 向resolve回调传递组件定义

resolve(resCom);

}, 1000);

});

2、局部

当然上面的代码也可以通过局部组件的方式使用:

var vm = new Vue({

el: '#app',

components: {

'async-example': function (resolve, reject) {

setTimeout(function () {

resolve(resCom);

// reject('加载失败描述内容');

}, 1000);

}

}

});

3、promise

在 Vue 中一样也可以通过 Promise 的方式来定义异步组件,这也是定义异步组件的第二种方式 Promise。

// 注册组件

var resCom = {

template: "#async-example"

};

var vm = new Vue({

el: "#app",

components:{

'async-example':function(){

return new Promise(function(resolve, reject){

setTimeout(function(){

resolve(resCom)

}, 1000);

});

}

}

})

4、webpack

require模式

Vue.component('async-webpack-example', function (resolve) {

// 这个特殊的 `require` 语法将会告诉 webpack

// 自动将你的构建代码切割成多个包,这些包

// 会通过 Ajax 请求加载

require(['./my-async-component'], resolve)

})

import()

Webpack + Vue-CLI是 Vue 所推崇的一种构建方式,同时 Webpack 也直接内建支持这种 Promise 的异步组件注册方式。当使用 Webpack 来构建正式项目时,我们也可以直接通过 import('./my-async-component') 来注册一个异步组件,import('./my-async-component') 会直接返回一个 Promise。

// 全局注册

Vue.component(

'async-webpack-example',

// 这个 `import` 函数会返回一个 `Promise` 对象。

() => import('./my-async-component')

)

// 局部注册

new Vue({

// ...

components: {

'my-component': () => import('./my-async-component')

}

})

5、高级异步组件应该如何定义:

const AsyncComponent = function(){

return {

// 需要加载的组件 (应该是一个 `Promise` 对象)

component: new Promise(function(resolve, reject){

setTimeout(function(){

resolve(resCom)

}, 2000);

});,

// 异步组件加载时使用的组件

loading: LoadingComponent,

// 加载失败时使用的组件

error: ErrorComponent,

// 展示加载时组件的延时时间。默认值是 200 (毫秒)

delay: 200,

// 如果提供了超时时间且组件加载也超时了,

// 则使用加载失败时使用的组件。默认值是:`Infinity`

// PS: 组件加载超时时间,超时表示加载失败,会展示ErrorComponent。

// 比如在这里当我们把 Promise 中的 setTimeout 改为 4000的时候,则会展示 ErrorComponent

timeout: 3000

}

}

var vm = new Vue({

el: "#app",

// 注意这里与之前的写法不同之处,是因为我们把这个方法提出去赋值给了AsyncComponent的变量

components:{

'async-example': AsyncComponent

}

})

10)动态组件和异步组件

1、上述内容可以通过 Vue 的 <component> 元素加一个特殊的 is 特性来实现

解析 DOM 模板时的注意事项:有些 HTML 元素,诸如 <ul>、<ol>、<table> 和 <select>,对于哪些元素可以出现在其内部是有严格限制的。而有些元素,诸如 <li>、<tr> 和 <option>,只能出现在其它某些特定的元素内部。这会导致我们使用这些有约束条件的元素时遇到一些问题。例如:

<table>

<blog-post-row></blog-post-row>

</table>

  • 这个自定义组件 <blog-post-row> 会被作为无效的内容提升到外部,并导致最终渲染结果出错。幸好这个特殊的 is 特性给了我们一个变通的办法:
  • <table>

<tr is="blog-post-row"></tr>

</table>

2、为了简化,Vue 允许你以一个工厂函数的方式定义你的组件,这个工厂函数会异步解析你的组件定义。

Vue 只有在这个组件需要被渲染时才会触发该工厂函数,且会把结果缓存起来供未来重渲染

  • 如你所见,这个工厂函数会收到一个 resolve 回调,这个回调函数会在你从服务器得到组件定义的时候被调用。你也可以调用 reject(reason) 来表示加载失败。
  • 这里的 setTimeout 是为了演示用的,如何获取组件取决于你自己。一个推荐的做法是将异步组件和 webpack 的 code-splitting 功能一起配合使用:

组件之间的循环引用

Vue异步组件处理路由组件加载状态的解决方案

  • 在大型单页面应用中,处于对性能的考虑和首屏加载速度的要求,我们一般都会使用webpack的代码分割和vue-router的路由懒加载功能将我们的代码分成一个个模块,并且只在需要的时候才从服务器加载一个模块。
  • 但是这种解决方案也有其问题,当网络环境较差时,我们去首次访问某个路由模块,由于加载该模块的资源需要一定的时间,那么该段时间内,我们的应用就会处于无响应的状态,用户体验极差。
  • 【解决方案】这种情况,我们一方面可以缩小路由模块代码的体积,静态资源使用cdn存储等方式缩短加载时间,另一方面则可以路由组件上使用异步组件,显示loading和error等状态,使用户能够得到清晰明了的操作反馈。
  • 【具体实现】声明方法,基于Vue动态组件工厂函数来返回一个Promise对象

    调用:

11)keep-alive

当组件切换时,你有时会想保持这些组件的状态,以避免反复重渲染导致的性能问题

注意这个 <keep-alive> 要求被切换到的组件都有自己的名字,不论是通过组件的 name 选项还是局部/全局注册。

组件中的name选项有什么作用?

当项目使用keep-alive时,可搭配组件name进行缓存过滤

//假设我们有个组件命名为detail,

//其中dom加载完毕后我们在钩子函数mounted中进行数据加载

export default {

name:'Detail'

},

mounted(){

this.getInfo();

},

methods:{

getInfo(){

axios.get('/xx/detail.json',{

params:{

id:this.$route.params.id

}

}).then(this.getInfoSucc)

}

}

/**

在App.vue中使用了keep-alive导致

我们第二次进入的时候页面不会重新请求,

即不会再次触发mounted函数。

有两个解决方案:

一个增加activated()函数,每次进入新页面的时候再获取一次数据。

还有个方案就是在keep-alive中增加一个过滤exclude:

*/

<div id="app">

<keep-alive exclude="Detail">

<router-view/>

</keep-alive>

</div>

3. 当你用vue-tools时:该调试工具里显示的组见名称是由vue中组件name决定的

作用

在组件切换过程中将状态保留在内存中,防止重复渲染DOM,减少加载时间及性能消耗,提高用户体验性。

原理

在 created 函数调用时将需要缓存的 VNode 节点保存在 this.cache 中/在 render(页面渲染) 时,如果 VNode 的 name 符合缓存条件(可以用 include 以及 exclude 控制),则会从 this.cache 中取出之前缓存的 VNode 实例进行渲染。

  • include - 字符串或正则表达式。只有名称匹配的组件会被缓存。
  • exclude - 字符串或正则表达式。任何名称匹配的组件都不会被缓存。
  • max - 数字。最多可以缓存多少组件实例。
  • 生命周期函数

1. activated

在 keep-alive 组件激活时调用

该钩子函数在服务器端渲染期间不被调用

2. deactivated

在 keep-alive 组件停用时调用

该钩子在服务器端渲染期间不被调用

被包含在 keep-alive 中创建的组件,会多出两个生命周期的钩子: activated 与 deactivated

使用 keep-alive 会将数据保留在内存中,如果要在每次进入页面的时候获取最新的数据,需要在 activated 阶段获取数据,承担原来 created 钩子函数中获取数据的任务。

根据条件缓存页面

12)import() 中的表达式

let path = '@/components/Message.vue';

let com = ()=>import(path)

let path = '@/components/Message.vue';

copyPath = path.substring(2,path.length)

let com = ()=>import(`@/copyPath`) 复制代码

1、import无法导入变量字符串作为路径
导入失败写法:let path = '@/components/Message.vue';

let com = ()=>import(path)

导入成功写法:let path = '@/components/Message.vue';

copyPath = path.substring(2,path.length)

let com = ()=>import(`@/${copyPath}`)

webpack目前不支持完全意义上的动态导入。
可以通过字符串模板功能帮助webpack识别主要路径,这样编译时会编译src下的模块,但组件的只有在被渲染时才加载,从而实现异步导入。
2、立即执行函数
异步组件的导入处理成立即执行函数,会在页面调用时导入的components函数作为变量直接被执行导入过程,避免导入的函数再次调用。

import() 必须至少包含一些关于模块的路径信息。打包可以限定于一个特定的目录或文件集,以便于在使用动态表达式时 - 包括可能在 import() 调用中请求的每个模块。例如, import(`./locale/${language}.json`) 会把 .locale 目录中的每个 .json 文件打包到新的 chunk 中。在运行时,计算完变量 language 后,就可以使用像 english.json 或 german.json 的任何文件。

13)Vue的computed和watch的细节全面分析

data: {

firstName: 'Foo',

lastName: 'Bar'

},

computed: {

fullName:{//fullName不可在data里面定义,

get(){//回调函数 当需要读取当前属性值是执行,根据相关数据计算并返回当前属性的值

return this.firstName + ' ' + this.lastName

},

set(val){//监视当前属性值的变化,当属性值发生变化时执行,更新相关的属性数据

//val就是fullName的最新属性值

console.log(val)

const names = val.split(' ');

console.log(names)

this.firstName = names[0];

this.lastName = names[1];

}

}

}

watch:{ secondChange:{ handler(oldVal,newVal){ console.log(oldVal) console.log(newVal) }, deep:true } },

2.console.log打印的结果,发现oldVal和newVal值是一样的,所以深度监听虽然可以监听到对象的变化,但是无法监听到具体对象里面那个属性的变化

3.oldVal和newVal值一样的原因是它们索引同一个对象/数组。Vue 不会保留修改之前值的副本
vm.$watch的深度监听

4.深度监听对应的函数名必须为handler,否则无效果,因为watcher里面对应的是对handler的调用

方法二:watch如果想要监听对象的单个属性的变化,必须用computed作为中间件转化,因为computed可以取到对应的属性值

data(){

return{

'first':{

second:0

}

}

},

computed:{

secondChange(){

return this.first.second

}

},

watch:{

secondChange(){

console.log('second属性值变化了')

}

},

传入的值想作为局部变量来使用,直接使用会

props:['listShop'],

data(){

return{}

},

created(){

this.listShop=30

}

报错

这个错误是说的避免直接修改父组件传入的值,因为会改变父组件的值,贴上官网介绍

简单数据类型解决方案:

所以可以在data中重新定义一个变量,改变指向,但是也只是针对简单数据类型,因为复杂数据类型栈存贮的是指针,

props:['listShop'],

data(){

return{

listShopChild:this.listShop

}

},

created(){

this.listShopChild=30

}

这样就可以愉快的更改传入的简单数据类型的数据啦!不会有任何报错,也不会影响父组件!

解决方案2

直接用computed改变

computed:{

listShopChild(){

return this.listShop

}

}

3.5 存在的问题

注意:此时用computed时,如果是数组this.$set(arr,1,true)对应的值耶不更新,

这个很坑,这个bug我找个很久

如果传入的值只是在data定义,并未在methods或生命周期钩子更改,直接改变也会报错

所以还是可以先用局部变量接收,再修改,这个坑比较多

应用4

监听vuex的state或者getters值的变化

computed:{

stateDemo(){

return this.$store.state.demoState;

}

}

watch:{

stateDemo(){

console.log('vuex变化啦')

}

}

javascript - Vue的computed和watch的细节全面分析 - 个人文章 - SegmentFault 思否

14)父子组件的生命周期执行流程

  • 虽然updated函数会在数据变化时被触发,但却不能准确的判断是那个属性值被改变,所以在实际情况中用computedwatch函数来监听属性的变化,并做一些其他的操作。
  • 所有的生命周期钩子自动绑定 this 上下文到实例中,所以不能使用箭头函数来定义一个生命周期方法 (例如 created: () => this.fetchTodos()),会导致this指向父级
  • 在使用vue-router时有时需要使用来缓存组件状态,这个时候created钩子就不会被重复调用了,如果我们的子组件需要在每次加载或切换状态的时候进行某些操作,可以使用activated钩子触发。
  • 父子组件的钩子并不会等待请求返回,请求是异步的,VUE设计也不能因为请求没有响应而不执行后面的钩子。所以,我们必须通过v-if来控制子组件钩子的执行时机

【2】如果子组件接受父组件传递过来的值经过转换展示,将prop赋值给子组件的data对象的属性,再用watch监听prop,prop改变后即时更新data对象上的属性。

注意:对象深层嵌套watch中加deep:true。

缺点: watch深度监听,如果监听的对象数据比较多,性能有损耗。

其实从上面看,就可以总结出来子孙组件的生命周期。

  • 如果子组件被v-if控制,且初始值为false,那么子组件一开始就不会被渲染,没有执行生命周期。
  • 如果子组件被v-if控制,且初始值为true,或者子组件被v-show控制,不论初始值,或者,子组件在父组件的created,beforemount阶段显示值为true,那么,子组件总是会在父组件beforemount之后开始执行生命周期。
  • 如果子组件在父组件的mounted阶段被渲染出来,这里会分两种情况,
    • 第一种是v-show,因为v-show无论为true还是false,它都会存在dom节点,在父组件生命周期的beforemount之后,子组件已经开始执行自己的生命周期,直到父组件mounted阶段之后,这一轮生命周期已经完成,data已经有了,页面也已经渲染完毕。所以,当v-show的子组件变为true显示后,会触发父组件的更新函数。
    • 第二种是v-if,因为v-if如果为false,是不会存在节点的,也就一开始不会显示,也就不会执行生命周期,所以,当v-if的子组件被渲染显示后,也会触发父组件的更新函数,不同的是,v-if子组件会在父组件的beforeupdate之后开始执行它的生命周期。

子组件修改父组件传递过来的props

【1】传递的数据类型是基础类型,子组件修改父组件传递过来的props,报错。

【2】传递的数据类型是引用类型,子组件将父组件传递过来的props作为本地数据,子组件修改父组件传递过来的props,父组件也会随之改变。

总结:

应用场景

在vue中父子组件是通过props传递数据的。通常有以下几种场景:

  • 子组件展示父组件传递过来的props,一般是字符串
  • 子组件接受父组件传递过来的props,作为本地数据使用
  • 子组件接受父组件的传递过来的props,进行转换后展示(计算得到某个值)
  • 子组件修改父组件传递过来的props

【1】子组件接受父组件传递过来的props仅作为展示时:无论什么数据类型,父组件改变,子组件随之改变。

对于这个场景,子组件只是展示,子组件随父组件变化是符合场景需求的

【2】子组件接受父组件传递过来的props作为本地数据时:prop为基础类型的值,父组件改变,子组件不变;prop为引用类型的值,父组件改变,子组件随之改变。

对于这个场景,子组件是在父组件的数据基础上进行加工,父组件数据改变,子组件是希望同步响应的,解决的方法:使用watch来监听prop。产生这个问题的原因是,因为生命周期的关系,组件的data属性,在reated,只在初始化的时候赋值了一次。

【3】子组件接受父组件传递过来的props作为计算属性时:无论什么数据类型,父组件改变,子组件随之改变。

【4】子组件修改父组件,基础类型的值报错;引用类型的值父组件也改变

注意 在父组件传递接口的数据给子组件时,一定要在子组件标签上加上v-if="传递的接口数据"

在父组件的created中发请求获取数据,通过prop传递给子组件。子组件在created或者mounted中拿父组件传递过来的数据 这样处理是有问题的。

在父组件调用接口传递数据给子组件时,接口响应显然是异步的。这会导致无论你在父组件哪个钩子发请求,在子组件哪个钩子接收数据。都是取不到的。当子组件的mounted都执行完之后,此时可能父组件的请求才返回数据。会导致,从父组件传递给子组件的数据是undefined。

解决方法1:

在渲染子组件的时候加上一个条件,data1是父组件调用接口返回的数据。当有数据的时候在去渲染子组件。这样就会形成天然的阻塞。在父组件的created中的请求返回数据后,才会执行子组件的created,mounted。最后执行父组件的mounted。

<div class="test">
    <children v-if="data1" :data="data1" ></children>
</div>复制代码

解决方法2:

在子组件中 watch 监听,父组件获取到值,这个值就会变化,自然是可以监听到的

从父组件点击调用接口并显示子组件,子组件拿到数据并监听在watch中调用方法并显示

props和data响应式、watch初始化..谁先执行【Vue父子组件生命执行周期】

可以看到首先是进行初始化生命周期,初始化事件中心,初始化渲染等操作,在created之前,这也就说明了为什么在created的时候无法进行dom操作

4、 总结:

  • 执行顺序beforeCreate ->inject -> Props -> Methods -> Data -> Computed -> Watch ->provide-> created

15)$nextTick理解

定义:在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。

所以就衍生出了这个获取更新后的DOM的Vue方法。所以放在Vue.nextTick()回调函数中的执行的应该是会对DOM进行操作的 js代码;

Vue在观察到数据变化时并不是直接更新DOM,而是开启一个队列,并缓冲在同一事件循环中发生的所有数据改变。在缓冲时会去除重复数据,从而避免不必要的计算和DOM操作。然后再下一个事件循环tick中,Vue刷新队列并执行实际(已去重的)工作。所以如果用一个for循环来动态改变数据100次,其实它只会应用最后一次改变,如果没有这种机制,DOM就要重绘100次,过于耗费资源。

Vue会根据当前浏览器环境优先使用原生的Promise.then和MutationObserver,如果都不支持,就会采用setTimeout代替。

事实上,在执行this.showText= true;时,div仍然还是没有被创建出来,直到下一个Vue事件循环时,才开始创建。 $nextTick就是用来指导什么时候DOM更新完成的

常用:created中dom操作

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

nicky_hb

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值