Vue3.0 为什么要用 proxy?
在 Vue2 中, 0bject.defineProperty 会改变原始数据,而 Proxy 是创建对象的虚拟表示,并提供 set 、get 和 deleteProperty 等处理器,这些处理器可在访问或修改原始对象上的属性时进行拦截,有以下特点∶
- 不需用使用
Vue.$set
或Vue.$delete
触发响应式。 - 全方位的数组变化检测,消除了Vue2 无效的边界情况。
- 支持 Map,Set,WeakMap 和 WeakSet。
Proxy 实现的响应式原理与 Vue2的实现原理相同,实现方式大同小异∶
- get 收集依赖
- Set、delete 等触发依赖
- 对于集合类型,就是对集合对象的方法做一层包装:原方法执行后执行依赖相关的收集或触发逻辑。
说说你对slot的理解?slot使用场景有哪些
一、slot是什么
在HTML中 slot
元素 ,作为 Web Components
技术套件的一部分,是Web组件内的一个占位符
该占位符可以在后期使用自己的标记语言填充
举个栗子
<template id="element-details-template">
<slot name="element-name">Slot template</slot>
</template>
<element-details>
<span slot="element-name">1</span>
</element-details>
<element-details>
<span slot="element-name">2</span>
</element-details>
template
不会展示到页面中,需要用先获取它的引用,然后添加到DOM
中,
customElements.define('element-details',
class extends HTMLElement {
constructor() {
super();
const template = document
.getElementById('element-details-template')
.content;
const shadowRoot = this.attachShadow({
mode: 'open'})
.appendChild(template.cloneNode(true));
}
})
在Vue
中的概念也是如此
Slot
艺名插槽,花名“占坑”,我们可以理解为solt
在组件模板中占好了位置,当使用该组件标签时候,组件标签里面的内容就会自动填坑(替换组件模板中slot
位置),作为承载分发内容的出口
二、使用场景
通过插槽可以让用户可以拓展组件,去更好地复用组件和对其做定制化处理
如果父组件在使用到一个复用组件的时候,获取这个组件在不同的地方有少量的更改,如果去重写组件是一件不明智的事情
通过slot
插槽向组件内部指定位置传递内容,完成这个复用组件在不同场景的应用
比如布局组件、表格列、下拉选、弹框显示内容等
使用vue渲染大量数据时应该怎么优化?说下你的思路!
分析
企业级项目中渲染大量数据的情况比较常见,因此这是一道非常好的综合实践题目。
回答
-
在大型企业级项目中经常需要渲染大量数据,此时很容易出现卡顿的情况。比如大数据量的表格、树
-
处理时要根据情况做不同处理:
-
可以采取分页的方式获取,避免渲染大量数据
-
vue-virtual-scroller (opens new window)等虚拟滚动方案,只渲染视口范围内的数据
-
如果不需要更新,可以使用v-once方式只渲染一次
-
通过v-memo (opens new window)可以缓存结果,结合
v-for
使用,避免数据变化时不必要的VNode
创建 -
可以采用懒加载方式,在用户需要的时候再加载数据,比如
tree
组件子树的懒加载
- 还是要看具体需求,首先从设计上避免大数据获取和渲染;实在需要这样做可以采用虚表的方式优化渲染;最后优化更新,如果不需要更新可以
v-once
处理,需要更新可以v-memo
进一步优化大数据更新性能。其他可以采用的是交互方式优化,无线滚动、懒加载等方案
scoped样式穿透
scoped
虽然避免了组件间样式污染,但是很多时候我们需要修改组件中的某个样式,但是又不想去除scoped
属性
- 使用
/deep/
<!-- Parent -->
<template>
<div class="wrap">
<Child />
</div>
</template>
<style lang="scss" scoped>
.wrap /deep/ .box{
background: red;
}
</style>
<!-- Child -->
<template>
<div class="box"></div>
</template>
- 使用两个
style
标签
<!-- Parent -->
<template>
<div class="wrap">
<Child />
</div>
</template>
<style lang="scss" scoped>
/* 其他样式 */
</style>
<style lang="scss">
.wrap .box{
background: red;
}
</style>
<!-- Child -->
<template>
<div class="box"></div>
</template>
Vue中v-html会导致哪些问题
- 可能会导致
xss
攻击 v-html
会替换掉标签内部的子元素
let template = require('vue-template-compiler');
let r = template.compile(`<div v-html="'<span>hello</span>'"></div>`)
// with(this){return _c('div',{domProps: {"innerHTML":_s('<span>hello</span>')}})}
console.log(r.render);
// _c 定义在core/instance/render.js
// _s 定义在core/instance/render-helpers/index,js
if (key === 'textContent' || key === 'innerHTML') {
if (vnode.children) vnode.children.length = 0
if (cur === oldProps[key]) continue // #6601 work around Chrome version <= 55 bug where single textNode // replaced by innerHTML/textContent retains its parentNode property
if (elm.childNodes.length === 1) {
elm.removeChild(elm.childNodes[0])
}
}
如果让你从零开始写一个vuex,说说你的思路
思路分析
这个题目很有难度,首先思考vuex
解决的问题:存储用户全局状态并提供管理状态API。
vuex
需求分析- 如何实现这些需求
回答范例
- 官方说
vuex
是一个状态管理模式和库,并确保这些状态以可预期的方式变更。可见要实现一个vuex
- 要实现一个
Store
存储全局状态 - 要提供修改状态所需API:
commit(type, payload), dispatch(type, payload)
- 实现
Store
时,可以定义Store
类,构造函数接收选项options
,设置属性state
对外暴露状态,提供commit
和dispatch
修改属性state
。这里需要设置state
为响应式对象,同时将Store
定义为一个Vue
插件 commit(type, payload)
方法中可以获取用户传入mutations
并执行它,这样可以按用户提供的方法修改状态。dispatch(type, payload)
类似,但需要注意它可能是异步的,需要返回一个Promise
给用户以处理异步结果
实践
Store
的实现:
class Store {
constructor(options) {
this.state = reactive(options.state)
this.options = options
}
commit(type, payload) {
this.options.mutations[type].call(this, this.state, payload)
}
}
vuex简易版
/**
* 1 实现插件,挂载$store
* 2 实现store
*/
let Vue;
class Store {
constructor(options) {
// state响应式处理
// 外部访问: this.$store.state.***
// 第一种写法
// this.state = new Vue({
// data: options.state
// })
// 第二种写法:防止外界直接接触内部vue实例,防止外部强行变更
this._vm = new Vue({
data: {
$$state: options.state
}
})
this._mutations = options.mutations
this._actions = options.actions
this.getters = {
}
options.getters && this.handleGetters(options.getters)
this.commit = this.commit.bind(this)
this.dispatch = this.dispatch.bind(this)
}
get state () {
return this._vm._data.$$state
}
set state (val) {
return new Error('Please use replaceState to reset state')
}
handleGetters (getters) {
Object.keys(getters).map(key => {
Object.defineProperty(this.getters, key, {
get: () => getters[key](this.state)
})
})
}
commit (type, payload) {
let entry = this._mutations[type]
if (!entry) {
return new Error(`${
type} is not defined`)
}
entry(this.state, payload)
}
dispatch (type, payload) {
let entry = this._actions[type]
if (!entry) {
return new Error(`${
type} i