computed与watch区别
computed(计算属性) | watch(侦听器) | |
---|---|---|
定义与用途 | 计算属性(computed)用于声明式地描述一些依赖响应式属性的计算值。当依赖的响应式属性值发生变化时,计算属性会重新求值。 | 侦听器(watch)允许你执行异步操作或开销较大的操作,以响应数据的变化。它更专注于数据变化后的响应,而不是数据本身。 |
返回值 | 有返回值,计算属性会基于其依赖的属性返回一个新的值。 | 监听函数不需要返回值,它主要执行一些操作,如发送请求、更新其他数据等。 |
缓存机制 | 支持缓存。只有当计算属性所依赖的响应式属性发生变化时,它才会重新计算。如果依赖没有变化,则直接使用缓存中的值。 | 不支持缓存。每次侦听的数据变化时,都会触发相应的操作,不论该操作是否已执行过。 |
异步支持 | 不支持异步操作。如果计算属性内部包含异步操作,则无法正确监听数据变化。 | 支持异步操作。你可以在侦听函数中执行异步操作,如发送网络请求。 |
触发时机 | 默认情况下,计算属性在组件实例化时立即计算一次(如果有依赖),并在其依赖的响应式属性变化时重新计算。 | 默认情况下,侦听器在组件实例化时不立即执行,除非设置了immediate: true 。当侦听的数据变化时,执行相应的操作。 |
深度监听 | 不支持深度监听(虽然可以通过getter函数内部逻辑实现复杂依赖的监听),但计算属性通常用于简单的依赖关系。 | 支持深度监听(通过deep: true ),可以监听对象内部属性的变化。但请注意,深度监听可能会带来性能问题。 |
适用场景 | 当一个值依赖于其他多个值时,使用计算属性可以简化模板中的表达式,并提高性能(通过缓存)。 | 当需要在数据变化时执行异步操作或复杂逻辑时,使用侦听器更为合适。 |
为什么Vue3性能比Vue2好
1. 响应式系统改进
- Proxy替代Object.defineProperty:Vue3使用ES6的Proxy对象替代了Vue2中的Object.defineProperty方法来实现响应式系统。Proxy可以拦截对象属性的读取、赋值等操作,无需像Object.defineProperty那样递归地对每个属性进行劫持,从而提高了性能。同时,Proxy还能监听属性的新增和删除,使得响应式系统更加完整和高效。
2. 模板编译优化
- 更高效的编译算法:Vue3对模板编译过程进行了优化,使得生成的渲染函数更加高效。这有助于减少在渲染过程中的计算量,提高渲染性能。
3. Diff算法优化
- 静态提升:Vue3在Diff算法中引入了静态提升技术。它会在初次渲染时,将不会变化的DOM节点标记为静态节点,并在后续的渲染过程中直接复用这些静态节点,避免了不必要的DOM操作。
- 更高效的Diff算法:Vue3采用了更Diff高效的算法来比较虚拟DOM之间的差异,并只更新**有:差异Vue的部分3,采用了而不是更加重新模块渲染整个组件。这种优化可以大大减少不必要的DOM操作,提高渲染性能。
4. 体积减小
- **模块化设计化的设计,将Vue的核心功能拆分成多个独立的模块。这使得开发者可以根据项目的实际需求,只引入需要的模块,从而减少最终打包体积。
- Tree Shaking:Vue3支持Tree Shaking,可以在打包过程中移除未使用的代码,进一步减小打包体积。
5. 更好的性能优化策略
- 事件侦听缓存:Vue3使用了事件侦听缓存机制,将侦听器缓存到指令实例中,减少了创建和销毁侦听器的次数,从而提高了性能。
- 减少不必要的计算:Vue3在内部实现上进行了大量的优化,减少了不必要的计算量,提高了整体性能。
6. 自定义渲染API
- 更灵活的渲染方式:Vue3引入了一个新的自定义渲染API,允许开发者更加灵活地控制组件的渲染方式。这有助于开发者根据项目需求,优化渲染过程,提高性能。
http VS https
数据发生变化,视图没更新
-
使用
this.$set
:this.$set
是Vue实例的一个方法,用于向响应式对象中添加一个属性,并确保新属性也是响应式的,同时触发视图更新。这个方法接受三个参数:目标对象、属性名(或索引)和新值。this.$set(this.someObject, 'newProperty', 'newValue'); this.$set(this.someArray, indexOfItem, newValue);
-
使用
Vue.set
(Vue 2.x):
在Vue 2.x中,如果你不在组件的方法中,而是需要在一个全局或混合的上下文中添加响应式属性,你可以使用Vue.set
。Vue 3.x移除了这个全局方法,但this.$set
仍然可用。 -
this.forceUpdate()
:this.forceUpdate()
强制Vue实例重新渲染。它仅影响实例本身和插入插槽内容的子组件,而不是所有子组件。虽然这可以解决更新问题,但它是一个比较粗暴的方法,因为它会忽略组件的所有条件渲染和watcher等,可能会导致不必要的性能开销。通常,应该避免使用此方法,除非确实没有其他办法。 -
深拷贝:
深拷贝对象或数组可能会解决某些更新问题,因为它创建了一个全新的对象或数组,然后Vue可以追踪这个新对象的变化。但是,这种方法通常不推荐用于解决响应式问题,因为它会破坏原有的响应式引用,并可能导致其他不可预见的问题(如失去对原始数据的引用)。此外,深拷贝也会带来性能开销。 -
确保数据在组件的
data
函数中返回:
确保你修改的数据是在Vue组件的data
函数中返回的对象或数组的一部分。如果数据是在data
函数外部定义的,那么Vue无法追踪其变化。 -
使用Vue 3的
reactive
或ref
:
如果你使用的是Vue 3,可以使用Composition API中的reactive
或ref
来确保你的数据是响应式的。reactive
用于对象,而ref
可以用于任何类型的值(包括对象和数组),但会返回一个响应式且可变的ref对象,你需要通过.value
来访问或修改其值。
封装的自定义指令
一、自定义指令的基本概念
Vue除了提供内置的指令(如v-model
、v-show
、v-if
等)外,还允许开发者注册自定义指令。自定义指令提供了一种机制,通过它可以封装一些DOM操作,以扩展Vue的功能。自定义指令以v-
为前缀(在注册时不需要前缀,但在使用时需要),后跟自定义的指令名。
二、注册自定义指令
自定义指令可以通过两种方式注册:全局注册和局部注册。
-
全局注册:
使用Vue.directive(id, [definition])
方法注册一个全局自定义指令。这个指令将在所有Vue实例中可用。
Vue.directive('my-directive', {
// 钩子函数
inserted: function (el) {
// 指令的逻辑
el.style.color = 'blue';
}
});
局部注册:
在Vue组件的directives
选项中注册一个局部自定义指令。这个指令仅在该组件及其子组件中可用。
export default {
directives: {
myDirective: {
inserted: function (el) {
el.style.color = 'red';
}
}
}
}
三、自定义指令的钩子函数
自定义指令可以包含几个可选的钩子函数,这些函数会在不同的时间点被调用:
- bind:只调用一次,指令绑定到第一次元素时调用。在这里可以进行一次性的初始化设置。
- inserted:被绑定元素插入父节点时调用(父节点存在即可调用,不必存在于document中)。
- update:当指令的绑定值发生变化时调用,但不论绑定值是否变化都会调用。
- componentUpdated:被绑定元素所在模板完成一次更新周期时调用。
- unbind:只调用一次,指令与元素解绑时调用。
四、自定义指令的参数和修饰符
自定义指令可以接受参数和修饰符,这些都可以在指令的钩子函数中以特殊的方式访问:
- 参数:通过
:
传递给指令,在钩子函数中可以通过binding.arg
访问。 - 修饰符:以
.
表示的特殊后缀,在钩子函数中通过binding.modifiers
访问,它是一个对象,包含了所有修饰符及其布尔值。
五、使用自定义指令
注册自定义指令后,可以在Vue模板中通过v-
前缀和指令名来使用它,同时可以传递参数和修饰符。
<div v-my-directive:arg.modifier="value"></div>
六、实际应用
自定义指令在Vue应用中有着广泛的应用场景,如:
- 聚焦元素:在页面加载或特定事件触发时,自动聚焦到某个元素。
- 动态样式:根据绑定值动态改变元素的样式。
- 权限控制:根据用户的权限动态显示或隐藏元素。
- 复杂DOM操作:封装一些复杂的DOM操作逻辑,提高代码的复用性和可维护性。
Vue的代理和环境变量
在Vue项目中,处理跨域请求和根据不同环境(如开发环境、测试环境、生产环境)使用不同的配置是常见的需求。Vue CLI 提供了便捷的方式来处理这些问题,主要通过配置代理(Proxy)和环境变量(Environment Variables)来实现。
代理(Proxy)
在开发模式下,Vue CLI 提供了一个基于 http-proxy-middleware
的开发服务器,它可以帮助你解决开发过程中的跨域问题。你可以在 vue.config.js
文件中配置代理规则。
-
创建或编辑
vue.config.js
文件:
在项目根目录下,如果没有vue.config.js
文件,则创建一个。这个文件是可选的,但如果你需要自定义Vue CLI的内部webpack配置,就需要这个文件。 -
配置代理:
在vue.config.js
文件中,你可以通过devServer.proxy
选项来设置代理规则。
// vue.config.js
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'http://example.com', // 目标API地址
changeOrigin: true, // 是否跨域
pathRewrite: {'^/api': ''} // 路径重写
}
}
}
}
-
这样配置后,当你访问
/api/some/path
时,实际上会请求到http://example.com/ some/path
。
环境变量
Vue CLI 项目支持两种环境变量:
-
客户端环境变量:以
VUE_APP_
开头的变量,会被webpack.DefinePlugin
静态嵌入到客户端侧的包中。你可以在public/index.html
、组件的.vue
文件或JavaScript文件中通过process.env.VUE_APP_XXX
访问它们。 -
Node.js 环境变量:在
vue.config.js
、插件选项、加载器选项等仅运行在构建过程中的代码中通过process.env.XXX
访问。
-
设置环境变量:
在.env
、.env.local
、.env.[mode]
和.env.[mode].local
文件中设置环境变量。其中,[mode]
是你运行Vue CLI命令时通过--mode
参数指定的模式,默认为development
、production
和test
。例如,在
.env.production
文件中设置:
VUE_APP_API_BASE_URL=https://api.example.com
在代码中访问环境变量:
在客户端代码中,你可以通过 process.env.VUE_APP_API_BASE_URL
访问上面设置的环境变量
axios.get(`${process.env.VUE_APP_API_BASE_URL}/some/path`)
.then(response => {
// 处理响应
})
.catch(error => {
// 处理错误
});
底层原理
- Webpack的DefinePlugin插件:
- Webpack的
DefinePlugin
允许你在编译时创建一些全局常量。这些常量在编译时会被静态地替换到你的代码中,因此它们可以作为编译时的环境变量来使用。 - 在Vue CLI项目中,当你设置以
VUE_APP_
开头的环境变量时,Vue CLI会自动利用Webpack的DefinePlugin
将这些变量注入到项目的构建过程中。
- Webpack的
- Vue CLI对环境变量的处理:
- Vue CLI在构建项目时,会读取项目根目录下的
.env
、.env.local
、.env.[mode]
和.env.[mode].local
等文件(其中[mode]
是构建模式,如development
、production
等)。 - 这些文件中以
VUE_APP_
开头的变量会被识别为客户端环境变量,并通过Webpack的DefinePlugin
注入到项目的构建配置中。 - 在构建过程中,Webpack会替换掉代码中所有对
process.env.VUE_APP_XXX
的引用,将其替换为相应的环境变量值。
- Vue CLI在构建项目时,会读取项目根目录下的
为什么能访问到配置的VUE_APP_BASE_URL
- 编译时替换:由于Webpack的
DefinePlugin
在编译时就已经将process.env.VUE_APP_XXX
替换为了具体的值,因此在运行时的代码中,process.env.VUE_APP_BASE_URL
等环境变量实际上已经被替换为了字符串常量。 - 客户端可用:这些替换后的字符串常量会包含在最终打包的JavaScript文件中,因此它们可以在客户端代码中直接访问和使用。
- 安全性:只有以
VUE_APP_
开头的变量才会被注入到客户端代码中,这是为了防止敏感信息(如数据库密码、API密钥等)被意外地暴露给客户端。
综上所述,Vue CLI通过结合Webpack的DefinePlugin
插件和自身的环境变量处理机制,实现了在Vue项目中配置和使用环境变量的功能。这使得开发者能够根据不同的构建模式(如开发模式、生产模式等)来使用不同的环境变量值,从而提高了项目的灵活性和可维护性