主题
Vue2.0 为什么不能检查数组的变化,该怎么解决?
Vue2.0 对于响应式数据的实现有一些不足:
- 无法检测数组/对象的新增
- 无法检测通过索引改变数组的操作
Vue 检测数据的变动是通过Object.defineProperty
实现的,所以无法监听数组的添加操作是可以理解的,因为是在构造函数中就已经为所有属性做了这个检测绑定操作。
js
// array
this.$set(array, index, data)
splice
// 对象
this.$set(obj, key ,value)
this.$watch('blog', this.getCatalog, {
deep: true
// immediate: true // 是否第一次触发
});
watch: {
'obj.name'(curVal, oldVal) {
// TODO
}
}
vue3 中的响应式设计原理?
- 在 Vue2 中,使用 ES5 的
Object.defineProperty()
函数实现; - 在 Vue3 中,使用 ES6 的
Proxy
和Reflect
实现;
js
function reactive(target) {
const handler = {
get(target, key, receiver) {
return Reflect.get(target, key, receiver);
},
set(target, key, value, receiver) {
return Reflect.set(target, key, value, receiver);
},
};
return new Proxy(target, handler);
}
const product = reactive({ price: 10, quantity: 2 });
product.price = 20;
console.log(product.price);
// 正在修改的数据: price ,值为: 20
// 正在读取的数据: price
// 20
说说 Vue 页面渲染流程?
创建实例 -> init -> 数据监听 -> 编译模板 -> VDOM -> Diff VDOM -> update -> render
Vue 页面渲染的基本流程:
创建 Vue 实例:
- 首先,用户通过
new Vue()
创建一个 Vue 实例,并传入一个选项对象,该对象包含了组件的数据(data)
、方法(methods)
、计算属性(computed)
、生命周期钩子(lifecycle hooks)
等。
- 首先,用户通过
初始化:
- Vue 实例创建后,Vue 开始初始化过程。这包括设置
数据监听
、解析选项对象
、调用生命周期钩子
(如beforeCreate
和created
)等。
- Vue 实例创建后,Vue 开始初始化过程。这包括设置
数据监听:
- Vue 使用 Object.defineProperty(在 Vue 2.x 中)或 Proxy(在 Vue 3.x 中)来实现
数据的响应式系统
。这意味着 Vue 会递归
地遍历传入的数据对象,并使用 getter 和 setter 来转换其属性,以便在数据发生变化时能够通知
Vue。
- Vue 使用 Object.defineProperty(在 Vue 2.x 中)或 Proxy(在 Vue 3.x 中)来实现
模板编译:
- 如果提供了
模板(template)
,Vue 会将其编译成渲染函数(render function)
。这个渲染函数是一个纯 JavaScript 函数,它基于 Vue 实例的状态描述了一个虚拟 DOM 树
的结构。
- 如果提供了
生成虚拟 DOM:
- 当组件的
状态发生变化时
(例如,通过用户交互或数据更新),Vue 会调用渲染函数来生成一个新的虚拟 DOM 树
。虚拟 DOM 是一个轻量级的 JavaScript 对象树,它是对真实 DOM 结构的抽象表示。
- 当组件的
比较虚拟 DOM:
- Vue 接下来会对比
新旧两个虚拟 DOM 树
,找出它们之间的差异
。这个过程是通过 Vue 的虚拟DOM diffing
算法实现的,该算法能够高效地计算出最小的 DOM 操作来更新视图
。
- Vue 接下来会对比
更新真实 DOM:
- 基于虚拟 DOM 比较的结果,Vue 会计算出必要的
DOM 操作(如添加、更新或删除节点)
,并应用到实际的 DOM 上,以反映组件的最新状态。
- 基于虚拟 DOM 比较的结果,Vue 会计算出必要的
重新渲染:
- 如果组件的状态再次发生变化,上述流程会再次执行,从生成新的虚拟 DOM 开始,直到更新真实 DOM。这个过程是自动的,并且是响应式的,即数据变化时视图会自动更新。
触发组件生命周期钩子:
- 在渲染过程的不同阶段,Vue 会触发相应的生命周期钩子,如
beforeMount
、mounted
、beforeUpdate
和updated
等,允许开发者在这些关键点执行自定义逻辑。
- 在渲染过程的不同阶段,Vue 会触发相应的生命周期钩子,如
在整个渲染流程中,Vue 的响应式系统是关键,它确保当数据发生变化时,视图能够得到及时更新。同时,Vue 的虚拟 DOM diffing 算法和高效的 DOM 操作使得这个过程既快速又高效。
Vue 3.0 中 Treeshaking特性是什么,并举例进行说明?
tree shaking 是一种通过清除多余代码方式
来优化项目打包体积的技术。Tree shaking 是基于 ES6 模板语法(import与export)
,主要是借助 ES6 模块的静态编译
思想,在编译时就能确定模块的依赖关系
,以及输入和输出的变量。
Vue 3.0 中的 treeshaking 是指在构建过程中,通过静态分析代码
,移除
未引用的代码(通常是代码中的导入),以减小最终的打包体积
。这是现代前端构建工具(如 Rollup, Webpack 2 及以上版本)所支持的一项特性。
要在 Vue 3.0 中启用 treeshaking,确保你的构建工具配置正确
,并且你的代码只导入它需要的 Vue 特性。例如,如果你只使用了 Vue 的响应式系统,你可以这样导入 Vue:
js
import { reactive } from 'vue';
const state = reactive({ count: 0 });
在这个例子中,reactive 是我们需要的响应式系统特性,其他如组件系统、生命周期钩子等特性都不会被包括在内,从而实现了 treeshaking。
此外,确保你的项目中使用了现代的打包工具,并且启用了它们的 treeshaking 功能。例如,在 Webpack 5 中,你只需确保在你的 webpack.config.js 文件中没有禁用 treeshaking:
js
module.exports = {
// ...
optimization: {
usedExports: true,
concatenateModules: true,
minimize: true,
},
// ...
};
vue中 computed 和 watch 区别 ?
js
<p>姓名:{{ fullName }}</p>
... ...
data: {
firstName: 'Leslie',
lastName: 'Chiang',
fullName: {
firstName: 'Leslie',
lastName: 'Chiang'
}
},
computed: {
// 只有当 computed 属性被使用后,才会执行 computed 的代码,在重复的调用中,只要依赖数据不变,直接取缓存中的计算结果。
// 只有依赖型数据发生改变,computed 才会重新计算。
fullName: function() {
return this.firstName + ' ' + this.lastName
},
// 在computed 中的属性都有一个 get 和一个 set 方法,当数据变化时,调用 set 方法。
// 下面我们通过计算属性的 getter/setter 方法来实现对属性数据的显示和监视,即双向绑定。
fullName: {
get() { //读取当前属性值的回调,根据相关的数据计算并返回当前属性的值
return this.firstName + ' ' + this.lastName
},
set(val) { // 当属性值发生改变时回调,更新相关的属性数据,val就是fullName的最新属性值
const names = val ? val.split(' ') : [];
this.firstName = names[0]
this.lastName = names[1]
}
}
},
watch: {
// 监听 data 中的 firstName,如果发生了变化,就把变化的值给 data 中的 fullName, val 就是 firstName 的最新值
firstName: function(val) {
this.fullName = val + ' ' + this.lastName
},
lastName: function(val) {
this.fullName = this.firstName + ' ' + val
},
fullName: {
handler(newVal, oldVal) {
// 打印出来的 newVal 和 oldVal 值是一样的,所以深度监听虽然可以监听到对象的变化,但是无法监听到对象里面哪个具体属性的变化。
// 这是因为它们的引用指向同一个对象/数组。Vue 不会保留变更之前值的副本。
console.log(newVal);
console.log(oldVal);
},
deep: true
}
}
若果要监听对象的单个属性的变化,有两种方法:
- 直接监听对象的属性
js
watch: {
fullName.firstName: function(newVal,oldVal) {
console.log(newVal,oldVal);
}
}
- 与 computed 属性配合使用,computed 返回想要监听的属性值,watch 用来监听
js
computed: {
firstNameChange() {
return this.fullName.firstName
}
},
watch: {
firstNameChange() {
console.log(this.fullName)
}
}
区别:
computed
初始化
显示或者相关的 data、props 等属性数据发生变化的时候调用;- 计算属性不在 data 中,它是
基于data 或 props 中的数据
通过计算得到的一个新值,这个新值根据已知值的变化而变化
; - 在 computed 属性对象中定义计算属性的方法,和取data对象里的数据属性一样,以属性访问的形式调用;
- 如果 computed 属性值是函数,那么默认会走 get 方法,必须要有一个返回值,函数的返回值就是属性的属性值;
- computed 属性值默认会
缓存计算结果
,在重复的调用中,只要依赖数据不变,直接取缓存中的计算结果,只有依赖型数据发生改变,computed 才会重新计算; - 在computed中的,属性都有一个
get
和一个set
方法,当数据变化时,调用 set 方法。
watch
- 主要用来监听某些特定数据的变化,从而进行某些具体的业务逻辑操作,可以看作是
computed
和methods
的结合体; - 可以监听的数据来源:data,props,computed内的数据;
- watch
支持异步
; 不支持缓存
,监听的数据改变,直接会触发相应的操作
;- 监听函数有两个参数,第一个参数是最新的值,第二个参数是输入之前的值,顺序一定是
新值
,旧值
。
- 主要用来监听某些特定数据的变化,从而进行某些具体的业务逻辑操作,可以看作是
Vue3.0 所采用的 Composition Api 与 Vue2.x 使用的 Options Api 有什么不同?
- Options Api
vue
<script>
export default {
// data() 返回的属性将会成为响应式的状态
// 并且暴露在 `this` 上
data() {
return {
count: 0
}
},
// methods 是一些用来更改状态与触发更新的函数
// 它们可以在模板中作为事件处理器绑定
methods: {
increment() {
this.count++
}
},
// 生命周期钩子会在组件生命周期的各个不同阶段被调用
// 例如这个函数就会在组件挂载完成后被调用
mounted() {
console.log(`The initial count is ${this.count}.`)
}
}
</script>
<template>
<button @click="increment">Count is: {{ count }}</button>
</template>
- Composition Api
vue
<script setup>
import { ref, onMounted } from 'vue'
// 响应式状态
const count = ref(0)
// 用来修改状态、触发更新的函数
function increment() {
count.value++
}
// 生命周期钩子
onMounted(() => {
console.log(`The initial count is ${count.value}.`)
})
</script>
<template>
<button @click="increment">Count is: {{ count }}</button>
</template>
- 在逻辑组织和逻辑复用方面,Composition API是优于Options API
- Composition API几乎是
函数
,会有更好的类型推断
。 - Composition API 对 tree-shaking 友好,代码也更
容易压缩
- Composition API中见不到 this 的使用,减少了
this
指向不明的情况
- Composition API几乎是
Vue3.0
更小
- Vue3移除一些不常用的 API, 引入tree-shaking,可以将无用模块“剪辑”,仅打包需要的,使打包的整体体积变小了
更快
- diff算法优化(静态标记)
- 静态提升(不参与更新的元素,会做静态提升,只会被创建一次,在渲染时直接复用)
- 事件监听缓存
- SSR优化(当静态内容大到一定量级时候,会用createStaticVNode方法在客户端去生成一个static node,这些静态node,会被直接innerHtml,就不需要创建对象,然后根据对象渲染)
TypeScript支持
- 提供了更好的类型检查,能支持复杂的类型推导
API设计一致性
提高自身可维护性
- 源码是通过
monorepo
的方式维护的,根据功能将不同的模块拆分到packages
- 源码是通过
开放更多底层功能
- 响应式库 reactivity 可以独立使用
响应式系统:
- vue2中采用 defineProperty 来劫持整个对象,然后进行
深度遍历
所有属性,给每个属性添加getter
和setter
,实现响应式。 - vue3采用 proxy 重写了响应式系统,因为 proxy 可以对
整个对象进行监听
,所以不需要深度遍历。(可以监听动态属性的添加,可以监听到数组的索引和数组length属性,可以监听删除属性)
- vue2中采用 defineProperty 来劫持整个对象,然后进行
处理 vue 项目中的错误的?
- 通过
axios
的interceptor
实现网络请求的response
先进行一层拦截
js
apiClient.interceptors.response.use(
response => {
return response;
},
error => {
if (error.response.status === 401) {
router.push({ name: "Login" });
} else {
message.error("出错了");
return Promise.reject(error);
}
}
);
- 设置全局错误处理函数
js
Vue.config.errorHandler = function (err, vm, info) {
}
- handleError 在需要捕获异常的地方调用,首先获取到报错的组件,之后递归查找当前组件的父组件,依次调用 errorCaptured 方法,在遍历调用完所有 errorCaptured 方法或 errorCaptured 方法有报错时,调用 globalHandleError 方法
- globalHandleError 调用全局的 errorHandler 方法,再通过 logError 判断环境输出错误信息
- invokeWithErrorHandling 更好的处理异步错误信息
- logError 判断环境,选择不同的抛错方式。非生产环境下,调用 warn 方法处理错误
Vue项目中如何解决跨域问题?
- JSONP
- CORS
- Proxy
js
// vue.config.js
amodule.exports = {
devServer: {
host: '127.0.0.1',
port: 8084,
// vue项目启动时自动打开浏览器
open: true,
proxy: {
'/api': {
// '/api'是代理标识,用于告诉node,url前面是/api的就是使用代理的
target: "http://xxx.xxx.xx.xx:8080", //目标地址,一般是指后台服务器地址
changeOrigin: true, //是否跨域
pathRewrite: {
// pathRewrite 的作用是把实际Request Url中的'/api'用""代替
'^/api': ""
}
}
}
}
}
js
// 服务端实现代理请求转发
var express = require('express');
const proxy = require('http-proxy-middleware')
const app = express()
app.use(express.static(__dirname + '/'))
app.use('/api', proxy({ target: 'http://localhost:4000', changeOrigin: false }));
module.exports = app
sh
server {
listen 80;
# server_name www.baidu.com;
location / {
root /var/www/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://127.0.0.1:3000;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
页面实现权限管理?
- 接口权限 (校验 token)
- 按钮权限 (后端返回)
- 菜单权限 (动态菜单)
- 路由权限(路由守卫)
说说vue中的diff算法
- 比较只会在同层级进行, 不会跨层级比较
- 在diff比较的过程中,循环从两边向中间比较
Vue 3.0 的 diff 算法相较于 Vue 2.x 有一些改进和优化,这些改进主要是为了支持 Vue 3.0 的新特性和提高性能。以下是 Vue 3.0 diff 算法的一些主要特点:
基于 Proxy 的响应式系统:Vue 3.0 引入了基于
Proxy
的响应式系统,这使得依赖追踪
和更新
变得更加精确
和高效
。在 Vue 3.0 中,每个响应式对象都被封装为一个 Proxy 对象,当对象的属性被访问或修改时,Proxy 可以自动触发相应的依赖更新。这种机制减少了不必要的计算和比较,提高了 diff 算法的效率。静态提升(Static Hoisting):Vue 3.0 引入了静态提升的概念,即在
编译阶段将静态内容(如字符串、数字等)提升到渲染函数之外
,避免在每次渲染时重复创建和销毁这些静态节点。这减少了不必要的内存分配和垃圾回收,提高了渲染性能。块级更新(Block Patching):Vue 3.0 采用了
块级更新
的策略,将节点按照不同的块进行组织和管理。在更新过程中,Vue 只会比较和更新发生变化的块,而不是整个虚拟 DOM 树。这减少了不必要的比较和计算
,提高了更新性能。动态子树更新(Dynamic Subtree Patching):Vue 3.0 支持
动态子树
的更新,即只更新发生变化的子树部分,而不是整个组件。这减少了不必要的渲染
和更新,提高了性能。优化列表渲染:Vue 3.0 对
列表渲染进行了优化
,采用了基于key
的节点复用策略
,提高了列表渲染的性能。同时,Vue 3.0 还支持对列表项进行批量
移动和删除操作
,进一步减少了不必要的 DOM 操作。缓存策略:Vue 3.0 引入了
缓存策略
,对于不常变化的节点,Vue 会将其缓存起来
,避免在每次渲染时重复创建和销毁这些节点。这减少了不必要的计算和内存分配,提高了性能。
总的来说,Vue 3.0 的 diff 算法在 Vue 2.x 的基础上进行了多方面的优化和改进,旨在提高渲染和更新的性能。这些改进使得 Vue 3.0 在处理大量数据或复杂 UI 时能够保持更好的性能表现。
Vue中 keep-alive 的理解
keep-alive 是 vue 中的内置组件,能在组件切换过程中将状态保留在内存中,防止重复渲染DOM
。
keep-alive可以设置以下props属性:
- include - 字符串或正则表达式。只有名称匹配的组件会被缓存
- exclude - 字符串或正则表达式。任何名称匹配的组件都不会被缓存
- max - 数字。最多可以缓存多少组件实例
缓存后如何获取数据:
- beforeRouteEnter
js
// 每次组件渲染的时候,都会执行beforeRouteEnter
beforeRouteEnter(to, from, next){
next(vm=>{
....
})
},
- actived
js
// 在keep-alive缓存的组件被激活的时候,都会执行actived钩子
activated(){
...
},
说说你对 slot 的理解?slot使用场景有哪些?
在HTML中 slot
元素 ,作为Web Components
技术套件的一部分,是Web组件内的一个占位符
。该占位符可以在后期使用自己的标记语言填充
。
使用场景:通过插槽可以让用户可以拓展组件,去更好地复用组件和对其做定制化处理;如果父组件在使用到一个复用组件的时候,获取这个组件在不同的地方有少量的更改,如果去重写组件是一件不明智的事情;通过 slot 插槽向组件内部指定位置传递内容,完成这个复用组件在不同场景的应用;比如布局组件、表格列、下拉选、弹框显示内容等。
slot可以分来以下三种:
- 默认插槽
- 具名插槽
- 作用域插槽
Vue 中的 $nextTick 有什么作用?
在下次 DOM 更新循环结束之后执行延迟回调
。在修改数据之后立即使用这个方法,获取更新后的 DOM
。
如果想要在修改数据后立刻得到更新后的DOM结构,可以使用 Vue.nextTick()
。
js
// 修改数据
vm.message = 'qzjiang'
// DOM 还没有更新, 原始的值
console.log(vm.$el.textContent)
Vue.nextTick(function () {
// DOM 更新了, 修改后的值
console.log(vm.$el.textContent)
})
Vue 组件间通信方式都有哪些?
- 通过
props
传递 - 通过
$emit
触发自定义事件 - 使用
ref
EventBus
$parent
或$root
attrs
与listeners
Provide
与Inject
Vuex
Vue生命周期的理解
生命周期 | 描述 |
---|---|
beforeCreate | 组件实例被创建之初(执行时组件实例还未创建,通常用于插件开发中执行一些初始化任务) |
created | 组件实例已经完全创建(组件初始化完毕,各种数据可以使用,常用于异步数据获取) |
beforeMount | 组件挂载之前(未执行渲染、更新,dom未创建) |
mounted | 组件挂载到实例上去之后(初始化结束,dom已创建,可用于获取访问数据和dom元素) |
beforeUpdate | 组件数据发生变化,更新之前(更新前,可用于获取更新前各种状态) |
updated | 数据数据更新之后(更新后,所有状态已是最新) |
beforeDestroy | 组件实例销毁之前(销毁前,可用于一些定时器或订阅的取消) |
destroyed | 组件实例销毁之后 |
activated | keep-alive 缓存的组件激活时 |
deactivated | keep-alive 缓存的组件停用时调用 |
errorCaptured | 捕获一个来自子孙组件的错误时被调用 |
beforeCreate -> created
- 初始化vue实例,进行数据观测
created
- 完成数据观测,属性与方法的运算,watch、event事件回调的配置
- 可调用 methods 中的方法,访问和修改 data 数据触发响应式渲染 dom,可通过 computed 和 watch 完成数据计算
- 此时 vm.$el 并没有被创建
created -> beforeMount
- 判断是否存在 el 选项,若不存在则停止编译,直到调用 vm.$mount(el) 才会继续编译
- 优先级:render > template > outerHTML
- vm.el 获取到的是挂载 DOM 的
beforeMount
- 在此阶段可获取到 vm.el
- 此阶段 vm.el 虽已完成 DOM 初始化,但并未挂载在 el 选项上
beforeMount -> mounted
- 此阶段 vm.el 完成挂载,vm.$el 生成的 DOM 替换了 el 选项所对应的 DOM
mounted
- vm.el 已完成 DOM 的挂载与渲染,此刻打印 vm.$el,发现之前的挂载点及内容已被替换成新的 DOM
beforeUpdate
- 更新的数据必须是被渲染在模板上的(el、template、render之一)
- 此时 view 层还未更新
- 若在 beforeUpdate 中再次修改数据,不会再次触发更新方法
updated
- 完成 view 层的更新
- 若在 updated 中再次修改数据,会再次触发更新方法(beforeUpdate、updated)
beforeDestroy
- 实例被销毁前调用,此时实例属性与方法仍可访问
destroyed
- 完全销毁一个实例。可清理它与其它实例的连接,解绑它的全部指令及事件监听器
- 并不能清除 DOM,仅仅销毁实例
Vue实例挂载的过程中发生了什么?
new Vue的时候调用会
调用_init
方法- 定义 $set、 $get 、$delete、$watch 等方法
- 定义 $on、$off、$emit、$off 等事件
- 定义 _update、$forceUpdate、$destroy生命周期
调用
$mount
进行页面的挂载挂载的时候主要是通过
mountComponent
方法定义 updateComponent 更新函数
执行 render 生成虚拟DOM
_update 将虚拟 DOM 生成真实 DOM 结构,并且渲染到页面中
Vue 模板是如何编译的?
模板解析 -> 生成AST -> 遍历AST, 静态标记(markStatic, markStaticRoots) -> render
Scoped Styles 为什么可以实现样式隔离?
在 Vue 中,作用域样式(Scoped Styles)
是通过以下原理实现的:
唯一选择器:
- 当 Vue 编译单文件组件时,在样式中使用 scoped 特性或 module 特性时,Vue 会为每个样式选择器生成一个唯一的属性选择器。
- 这里的唯一选择器是类似于 [data-v-xxxxxxx] 的属性选择器,其中 xxxxxxx 是一个唯一的标识符。
编译时转换:
- Vue 在编译过程中会解析单文件组件的模板,并对样式进行处理。
- 对于具有 scoped 特性的样式,Vue 会将选择器转换为带有唯一属性选择器的形式,例如 .class 会被转换为 .class[data-v-xxxxxxx]。
- 对于具有 module 特性的样式,Vue 会为每个选择器生成一个唯一的类名,并将类名与元素关联起来。
渲染时应用:
- 在组件渲染过程中,Vue 会为组件的根元素添加一个属性值为唯一标识符的属性,例如 data-v-xxxxxxx。
- 当组件渲染完成后,样式选择器中的唯一属性选择器或唯一类名将与组件根元素的属性匹配,从而实现样式的隔离。
- 这样,只有具有相同属性值的元素才会应用相应的样式,避免了样式冲突和泄漏。
打破隔离限制:
- 使用 /deep/ 或 ::v-deep:
- 使用全局样式
- 使用类名继承(在子组件的
<style>
标签中使用@extend
来继承父组件或其他组件的样式)
Vue中给对象添加新属性时,界面不刷新怎么办?
Vue.set()
Object.assign()
$forcecUpdated()
Vue常用的修饰符有哪些?
表单修饰符
html
<input type="text" v-model.lazy="value">
<input type="text" v-model.trim="value">
<input v-model.number="age" type="number">
事件修饰符
html
<button @click.stop="shout(1)">ok</button>
<button @click.once="shout(1)">ok</button>
<form v-on:submit.prevent="onSubmit"></form>
<div v-on:click.self="doThat">...</div>
<div v-on:scroll.passive="onScroll">...</div>
<my-component v-on:click.native="doSomething"></my-component>
<div @click.capture="shout(1)">
obj1
<div @click.capture="shout(2)">
obj2
<div @click="shout(3)">
obj3
<div @click="shout(4)">
obj4
</div>
</div>
</div>
</div>
鼠标按键修饰符
html
<button @click.left="click()">click</button>
<button @click.right="click()">click</button>
<button @click.middle="click()">click</button>
键值修饰符
html
<input type="text" @keyup.keyCode="submit()">
v-bind修饰符
html
<comp :myMessage.sync="bar"></comp>
// 设置自定义标签属性,避免暴露数据,防止污染HTML结构
<input id="uid" title="title1" value="1" :index.prop="index">
// 将命名变为驼峰命名法,如将 view-Box属性名转换为 viewBox
<svg :viewBox="viewBox"></svg>
为什么Vue中的 v-if 和 v-for 不建议一起用?
- 不要把 v-if 和 v-for 同时用在同一个元素上,带来性能方面的浪费(
每次渲染都会先循环再进行条件判断
) - 如果避免出现这种情况,则在外层嵌套
template(页面渲染不生成dom节点
),在这一层进行 v-if 判断,然后在内部进行 v-for 循环
vue
<template v-if="isShow">
<p v-for="item in items">
</template>
- 如果条件出现在循环内部,可通过计算属性
computed
提前过滤掉那些不需要显示的项
js
computed: {
items: function() {
return this.dataList.filter(function (it) {
return it.show
})
}
}
刷新浏览器后,Vuex 的数据是否存在?如何解决?
store 里的数据是保存在运行内存中的
,当页面刷新时,页面会重新加载vue实例,store里面的数据就会被重新赋值初始化。
使用 localStorage
或者 sessionStroage。
VNode 有哪些属性?
常见的属性如下:
- __v_isVNode: true,内部属性,有该属性表示为Vnode
- type: VNodeTypes,虚拟节点的类型
- props: (VNodeProps & ExtraProps) | null,虚拟节点的props
- key: string | number | null,虚拟阶段的key,可用于diff
- ref: VNodeNormalizedRef | null,虚拟阶段的引用
- children: VNodeNormalizedChildren,子节点
- component: ComponentInternalInstance | null,组件实例
- el: HostNode | null,宿主阶段
- target: HostElement | null ,teleport target 传送的目标
vue-loader 做了哪些事情?
通过 vue-loader, webpack 可以将.vue
文件 转化为浏览器可识别的javascript。
工作流程,分为以下几个步骤:
- 将一个 .vue 文件 切割成
template、script、styles
三个部分。 - template 部分 通过
compile
生成render
、staticRenderFns
。 - 获取
script
部分 返回的配置项对象scriptExports
。 - styles 部分,会通过
css-loader
、vue-style-loade
r, 添加到head
中, 或者通过css-loader
、MiniCssExtractPlugin
提取到一个 公共的css文件 中。 - 使用 vue-loader 提供的
normalizeComponent
方法, 合并scriptExports、render、staticRenderFns,
返回构建vue组件需要的配置项对象option
。