你有对 Vue 我的项目进行哪些优化?

(1)代码层面的优化

  • v-if 和 v-show 辨别应用场景
  • computed 和 watch 辨别应用场景
  • v-for 遍历必须为 item 增加 key,且防止同时应用 v-if
  • 长列表性能优化
  • 事件的销毁
  • 图片资源懒加载
  • 路由懒加载
  • 第三方插件的按需引入
  • 优化有限列表性能
  • 服务端渲染 SSR or 预渲染

(2)Webpack 层面的优化

  • Webpack 对图片进行压缩
  • 缩小 ES6 转为 ES5 的冗余代码
  • 提取公共代码
  • 模板预编译
  • 提取组件的 CSS
  • 优化 SourceMap
  • 构建后果输入剖析
  • Vue 我的项目的编译优化

(3)根底的 Web 技术的优化

  • 开启 gzip 压缩
  • 浏览器缓存
  • CDN 的应用
  • 应用 Chrome Performance 查找性能瓶颈

Vuex中action和mutation的区别

mutation中的操作是一系列的同步函数,用于批改state中的变量的的状态。当应用vuex时须要通过commit来提交须要操作的内容。mutation 十分相似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是理论进行状态更改的中央,并且它会承受 state 作为第一个参数:

const store = new Vuex.Store({  state: {    count: 1  },  mutations: {    increment (state) {      state.count++      // 变更状态    }  }})

当触发一个类型为 increment 的 mutation 时,须要调用此函数:

store.commit('increment')

而Action相似于mutation,不同点在于:

  • Action 能够蕴含任意异步操作。
  • Action 提交的是 mutation,而不是间接变更状态。
const store = new Vuex.Store({  state: {    count: 0  },  mutations: {    increment (state) {      state.count++    }  },  actions: {    increment (context) {      context.commit('increment')    }  }})

Action 函数承受一个与 store 实例具备雷同办法和属性的 context 对象,因而你能够调用 context.commit 提交一个 mutation,或者通过 context.state 和 context.getters 来获取 state 和 getters。
所以,两者的不同点如下:

  • Mutation专一于批改State,实践上是批改State的惟一路径;Action业务代码、异步申请。
  • Mutation:必须同步执行;Action:能够异步,但不能间接操作State。
  • 在视图更新时,先触发actions,actions再触发mutation
  • mutation的参数是state,它蕴含store中的数据;store的参数是context,它是 state 的父级,蕴含 state、getters

函数式组件劣势和原理

函数组件的特点

  1. 函数式组件须要在申明组件是指定 functional:true
  2. 不须要实例化,所以没有this,this通过render函数的第二个参数context来代替
  3. 没有生命周期钩子函数,不能应用计算属性,watch
  4. 不能通过$emit 对外裸露事件,调用事件只能通过context.listeners.click的形式调用内部传入的事件
  5. 因为函数式组件是没有实例化的,所以在内部通过ref去援用组件时,理论援用的是HTMLElement
  6. 函数式组件的props能够不必显示申明,所以没有在props外面申明的属性都会被主动隐式解析为prop,而一般组件所有未声明的属性都解析到$attrs外面,并主动挂载到组件根元素下面(能够通过inheritAttrs属性禁止)

长处

  1. 因为函数式组件不须要实例化,无状态,没有生命周期,所以渲染性能要好于一般组件
  2. 函数式组件构造比较简单,代码构造更清晰

应用场景:

  • 一个简略的展现组件,作为容器组件应用 比方 router-view 就是一个函数式组件
  • “高阶组件”——用于接管一个组件作为参数,返回一个被包装过的组件

例子

Vue.component('functional',{ // 构造函数产生虚构节点的    functional:true, // 函数式组件 // data={attrs:{}}    render(h){        return h('div','test')    }})const vm = new Vue({    el: '#app'})

源码相干

// functional componentif (isTrue(Ctor.options.functional)) { // 带有functional的属性的就是函数式组件  return createFunctionalComponent(Ctor, propsData, data, context, children)}// extract listeners, since these needs to be treated as// child component listeners instead of DOM listenersconst listeners = data.on // 处理事件// replace with listeners with .native modifier// so it gets processed during parent component patch.data.on = data.nativeOn // 解决原生事件// install component management hooks onto the placeholder nodeinstallComponentHooks(data) // 装置组件相干钩子 (函数式组件没有调用此办法,从而性能高于一般组件)

v-model实现原理

咱们在 vue 我的项目中次要应用 v-model 指令在表单 inputtextareaselect 等元素上创立双向数据绑定,咱们晓得 v-model 实质上不过是语法糖(能够看成是value + input办法的语法糖),v-model 在外部为不同的输出元素应用不同的属性并抛出不同的事件:
  • texttextarea 元素应用 value 属性和 input 事件
  • checkboxradio 应用 checked 属性和 change 事件
  • select 字段将 value 作为 prop 并将 change 作为事件

所以咱们能够v-model进行如下改写:

<input v-model="sth" /><!-- 等同于 --><input :value="sth" @input="sth = $event.target.value" />
当在input元素中应用v-model实现双数据绑定,其实就是在输出的时候触发元素的input事件,通过这个语法糖,实现了数据的双向绑定
  • 这个语法糖必须是固定的,也就是说属性必须为value,办法名必须为:input
  • 晓得了v-model的原理,咱们能够在自定义组件上实现v-model
//Parent<template>  {{num}}  <Child v-model="num"></template>export default {  data(){    return {      num: 0    }  }}//Child<template>  <div @click="add">Add</div></template>export default {  props: ['value'], // 属性必须为value  methods:{    add(){      // 办法名为input      this.$emit('input', this.value + 1)    }  }}

原理

会将组件的 v-model 默认转化成value+input

const VueTemplateCompiler = require('vue-template-compiler'); const ele = VueTemplateCompiler.compile('<el-checkbox v-model="check"></el- checkbox>'); // 察看输入的渲染函数:// with(this) { //     return _c('el-checkbox', { //         model: { //             value: (check), //             callback: function ($$v) { check = $$v }, //             expression: "check" //         } //     }) // }
// 源码地位 core/vdom/create-component.js line:155function transformModel (options, data: any) {     const prop = (options.model && options.model.prop) || 'value'     const event = (options.model && options.model.event) || 'input'     ;(data.attrs || (data.attrs = {}))[prop] = data.model.value     const on = data.on || (data.on = {})     const existing = on[event]     const callback = data.model.callback     if (isDef(existing)) {         if (Array.isArray(existing) ? existing.indexOf(callback) === -1 : existing !== callback ) {            on[event] = [callback].concat(existing)         }     } else {         on[event] = callback     } }

原生的 v-model,会依据标签的不同生成不同的事件和属性

const VueTemplateCompiler = require('vue-template-compiler'); const ele = VueTemplateCompiler.compile('<input v-model="value"/>');// with(this) { //     return _c('input', { //         directives: [{ name: "model", rawName: "v-model", value: (value), expression: "value" }], //         domProps: { "value": (value) },//         on: {"input": function ($event) { //             if ($event.target.composing) return;//             value = $event.target.value//         }//         }//     })// }
编译时:不同的标签解析出的内容不一样 platforms/web/compiler/directives/model.js
if (el.component) {     genComponentModel(el, value, modifiers) // component v-model doesn't need extra runtime     return false } else if (tag === 'select') {     genSelect(el, value, modifiers) } else if (tag === 'input' && type === 'checkbox') {     genCheckboxModel(el, value, modifiers) } else if (tag === 'input' && type === 'radio') {     genRadioModel(el, value, modifiers) } else if (tag === 'input' || tag === 'textarea') {     genDefaultModel(el, value, modifiers) } else if (!config.isReservedTag(tag)) {     genComponentModel(el, value, modifiers) // component v-model doesn't need extra runtime     return false }
运行时:会对元素解决一些对于输入法的问题 platforms/web/runtime/directives/model.js
inserted (el, binding, vnode, oldVnode) {     if (vnode.tag === 'select') { // #6903     if (oldVnode.elm && !oldVnode.elm._vOptions) {         mergeVNodeHook(vnode, 'postpatch', () => {             directive.componentUpdated(el, binding, vnode)         })     } else {         setSelected(el, binding, vnode.context)     }    el._vOptions = [].map.call(el.options, getValue)     } else if (vnode.tag === 'textarea' || isTextInputType(el.type)) {         el._vModifiers = binding.modifiers         if (!binding.modifiers.lazy) {             el.addEventListener('compositionstart', onCompositionStart)             el.addEventListener('compositionend', onCompositionEnd)             // Safari < 10.2 & UIWebView doesn't fire compositionend when             // switching focus before confirming composition choice             // this also fixes the issue where some browsers e.g. iOS Chrome            // fires "change" instead of "input" on autocomplete.             el.addEventListener('change', onCompositionEnd) /* istanbul ignore if */             if (isIE9) {                 el.vmodel = true             }        }    }}

v-if和v-show区别

  • v-show暗藏则是为该元素增加css--display:nonedom元素仍旧还在。v-if显示暗藏是将dom元素整个增加或删除
  • 编译过程:v-if切换有一个部分编译/卸载的过程,切换过程中适合地销毁和重建外部的事件监听和子组件;v-show只是简略的基于css切换
  • 编译条件:v-if是真正的条件渲染,它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。只有渲染条件为假时,并不做操作,直到为真才渲染
  • v-showfalse变为true的时候不会触发组件的生命周期
  • v-iffalse变为true的时候,触发组件的beforeCreatecreatebeforeMountmounted钩子,由true变为false的时候触发组件的beforeDestorydestoryed办法
  • 性能耗费:v-if有更高的切换耗费;v-show有更高的初始渲染耗费

v-show与v-if的应用场景

  • v-ifv-show 都能管制dom元素在页面的显示
  • v-if 相比 v-show 开销更大的(间接操作dom节点减少与删除)
  • 如果须要十分频繁地切换,则应用 v-show 较好
  • 如果在运行时条件很少扭转,则应用 v-if 较好

v-show与v-if原理剖析

  1. v-show原理

不论初始条件是什么,元素总是会被渲染

咱们看一下在vue中是如何实现的

代码很好了解,有transition就执行transition,没有就间接设置display属性

// https://github.com/vuejs/vue-next/blob/3cd30c5245da0733f9eb6f29d220f39c46518162/packages/runtime-dom/src/directives/vShow.tsexport const vShow: ObjectDirective<VShowElement> = {  beforeMount(el, { value }, { transition }) {    el._vod = el.style.display === 'none' ? '' : el.style.display    if (transition && value) {      transition.beforeEnter(el)    } else {      setDisplay(el, value)    }  },  mounted(el, { value }, { transition }) {    if (transition && value) {      transition.enter(el)    }  },  updated(el, { value, oldValue }, { transition }) {    // ...  },  beforeUnmount(el, { value }) {    setDisplay(el, value)  }}
  1. v-if原理

v-if在实现上比v-show要简单的多,因为还有else else-if 等条件须要解决,这里咱们也只摘抄源码中解决 v-if 的一小部分

返回一个node节点,render函数通过表达式的值来决定是否生成DOM

// https://github.com/vuejs/vue-next/blob/cdc9f336fd/packages/compiler-core/src/transforms/vIf.tsexport const transformIf = createStructuralDirectiveTransform(  /^(if|else|else-if)$/,  (node, dir, context) => {    return processIf(node, dir, context, (ifNode, branch, isRoot) => {      // ...      return () => {        if (isRoot) {          ifNode.codegenNode = createCodegenNodeForBranch(            branch,            key,            context          ) as IfConditionalExpression        } else {          // attach this branch's codegen node to the v-if root.          const parentCondition = getParentCondition(ifNode.codegenNode!)          parentCondition.alternate = createCodegenNodeForBranch(            branch,            key + ifNode.branches.length - 1,            context          )        }      }    })  })

Vue要做权限治理该怎么做?管制到按钮级别的权限怎么做?

剖析

  • 综合实际题目,理论开发中常常须要面临权限治理的需要,考查理论利用能力。
  • 权限治理个别需要是两个:页面权限和按钮权限,从这两个方面阐述即可。

思路

  • 权限治理需要剖析:页面和按钮权限
  • 权限治理的实现计划:分后端计划和前端计划论述
  • 说说各自的优缺点

答复范例

  1. 权限治理个别需要是页面权限和按钮权限的治理
  2. 具体实现的时候分后端和前端两种计划:
  3. 前端计划 会把所有路由信息在前端配置,通过路由守卫要求用户登录,用户登录后依据角色过滤出路由表。比方我会配置一个asyncRoutes数组,须要认证的页面在其路由的meta中增加一个roles字段,等获取用户角色之后取两者的交加,若后果不为空则阐明能够拜访。此过滤过程完结,剩下的路由就是该用户能拜访的页面,最初通过router.addRoutes(accessRoutes)形式动静增加路由即可
  • 后端计划 会把所有页面路由信息存在数据库中,用户登录的时候依据其角色查问失去其能拜访的所有页面路由信息返回给前端,前端再通过addRoutes动静增加路由信息
  • 按钮权限的管制通常会实现一个指令,例如v-permission,将按钮要求角色通过值传给v-permission指令,在指令的moutned钩子中能够判断以后用户角色和按钮是否存在交加,有则保留按钮,无则移除按钮
  • 纯前端计划的长处是实现简略,不须要额定权限治理页面,然而保护起来问题比拟大,有新的页面和角色需要就要批改前端代码从新打包部署;服务端计划就不存在这个问题,通过专门的角色和权限治理页面,配置页面和按钮权限信息到数据库,利用每次登陆时获取的都是最新的路由信息,堪称一劳永逸!

可能的诘问

  1. 相似Tabs这类组件能不能应用v-permission指令实现按钮权限管制?
<el-tabs>   <el-tab-pane label="⽤户治理" name="first">⽤户治理</el-tab-pane>     <el-tab-pane label="⻆⾊治理" name="third">⻆⾊治理</el-tab-pane></el-tabs>
  1. 服务端返回的路由信息如何增加到路由器中?
// 前端组件名和组件映射表const map = {  //xx: require('@/views/xx.vue').default // 同步的⽅式  xx: () => import('@/views/xx.vue') // 异步的⽅式}// 服务端返回的asyncRoutesconst asyncRoutes = [  { path: '/xx', component: 'xx',... }]// 遍历asyncRoutes,将component替换为map[component]function mapComponent(asyncRoutes) {  asyncRoutes.forEach(route => {    route.component = map[route.component];    if(route.children) {      route.children.map(child => mapComponent(child))    }    })}mapComponent(asyncRoutes)

你是怎么解决vue我的项目中的谬误的?

剖析

  • 这是一个综合利用题目,在我的项目中咱们经常须要将App的异样上报,此时错误处理就很重要了。
  • 这里要辨别谬误的类型,针对性做收集。
  • 而后是将收集的的错误信息上报服务器。

思路

  • 首先辨别谬误类型
  • 依据谬误不同类型做相应收集
  • 收集的谬误是如何上报服务器的

答复范例

  1. 利用中的谬误类型分为"接口异样"和“代码逻辑异样
  2. 咱们须要依据不同谬误类型做相应解决:接口异样是咱们申请后端接口过程中产生的异样,可能是申请失败,也可能是申请取得了服务器响应,然而返回的是谬误状态。以Axios为例,这类异样咱们能够通过封装Axios,在拦截器中对立解决整个利用中申请的谬误。代码逻辑异样是咱们编写的前端代码中存在逻辑上的谬误造成的异样,vue利用中最常见的形式是应用全局谬误处理函数app.config.errorHandler收集谬误
  3. 收集到谬误之后,须要对立解决这些异样:剖析谬误,获取须要错误信息和数据。这里应该无效辨别谬误类型,如果是申请谬误,须要上报接口信息,参数,状态码等;对于前端逻辑异样,获取谬误名称和详情即可。另外还能够收集利用名称、环境、版本、用户信息,所在页面等。这些信息能够通过vuex存储的全局状态和路由信息获取

实际

axios拦截器中解决捕捉异样:

// 响应拦截器instance.interceptors.response.use(  (response) => {    return response.data;  },  (error) => {    // 存在response阐明服务器有响应    if (error.response) {      let response = error.response;      if (response.status >= 400) {        handleError(response);      }    } else {      handleError(null);    }    return Promise.reject(error);  },);

vue中全局捕捉异样:

import { createApp } from 'vue'const app = createApp(...)app.config.errorHandler = (err, instance, info) => {  // report error to tracking services}

解决接口申请谬误:

function handleError(error, type) {  if(type == 1) {    // 接口谬误,从config字段中获取申请信息    let { url, method, params, data } = error.config    let err_data = {       url, method,       params: { query: params, body: data },       error: error.data?.message || JSON.stringify(error.data),    })  }}

解决前端逻辑谬误:

function handleError(error, type) {  if(type == 2) {    let errData = null    // 逻辑谬误    if(error instanceof Error) {      let { name, message } = error      errData = {        type: name,        error: message      }    } else {      errData = {        type: 'other',        error: JSON.strigify(error)      }    }  }}

参考:前端vue面试题具体解答

Vue3.2 setup 语法糖汇总

提醒:vue3.2 版本开始能力应用语法糖!

Vue3.0 中变量必须 return 进去, template 中能力应用;而在 Vue3.2 中只须要在 script 标签上加上 setup 属性,无需 returntemplate 便可间接应用,十分的香啊!

1. 如何应用setup语法糖

只需在 script 标签上写上 setup

<template></template><script setup></script><style scoped lang="less"></style>

2. data数据的应用

因为 setup 不需写 return ,所以间接申明数据即可

<script setup>import {  ref,  reactive,  toRefs,} from 'vue'const data = reactive({  patternVisible: false,  debugVisible: false,  aboutExeVisible: false,})const content = ref('content')//应用toRefs解构const { patternVisible, debugVisible, aboutExeVisible } = toRefs(data)</script>

3. method办法的应用

<template >  <button @click="onClickHelp">帮忙</button></template><script setup>import {reactive} from 'vue'const data = reactive({  aboutExeVisible: false,})// 点击帮忙const onClickHelp = () => {  console.log(`帮忙`)  data.aboutExeVisible = true}</script>

4. watchEffect的应用

<script setup>import {  ref,  watchEffect,} from 'vue'let sum = ref(0)watchEffect(()=>{  const x1 = sum.value  console.log('watchEffect所指定的回调执行了')})</script>

5. watch的应用

<script setup>import {  reactive,  watch,} from 'vue'//数据let sum = ref(0)let msg = ref('hello')let person = reactive({  name:'张三',  age:18,  job:{    j1:{      salary:20    }  }})// 两种监听格局watch([sum,msg],(newValue,oldValue)=>{    console.log('sum或msg变了',newValue,oldValue)  },  {immediate:true})watch(()=>person.job,(newValue,oldValue)=>{  console.log('person的job变动了',newValue,oldValue)},{deep:true}) </script>

6. computed计算属性的应用

computed 计算属性有两种写法(简写和思考读写的残缺写法)

<script setup>import {  reactive,  computed,} from 'vue'// 数据let person = reactive({  firstName:'poetry',  lastName:'x'})// 计算属性简写person.fullName = computed(()=>{  return person.firstName + '-' + person.lastName})// 残缺写法person.fullName = computed({  get(){    return person.firstName + '-' + person.lastName  },  set(value){    const nameArr = value.split('-')    person.firstName = nameArr[0]    person.lastName = nameArr[1]  }})</script>

7. props父子传值的应用

父组件代码如下(示例):

<template>  <child :name='name'/>  </template><script setup>  import {ref} from 'vue'  // 引入子组件  import child from './child.vue'  let name= ref('poetry')</script>

子组件代码如下(示例):

<template>  <span>{{props.name}}</span></template><script setup>import { defineProps } from 'vue'// 申明propsconst props = defineProps({  name: {    type: String,    default: 'poetries'  }})  // 或者//const props = defineProps(['name'])</script>

8. emit子父传值的应用

父组件代码如下(示例):

<template>  <AdoutExe @aboutExeVisible="aboutExeHandleCancel" /></template><script setup>import { reactive } from 'vue'// 导入子组件import AdoutExe from '../components/AdoutExeCom'const data = reactive({  aboutExeVisible: false, })// content组件ref// 对于零碎暗藏const aboutExeHandleCancel = () => {  data.aboutExeVisible = false}</script>

子组件代码如下(示例):

<template>  <a-button @click="isOk">    确定  </a-button></template><script setup>import { defineEmits } from 'vue';// emitconst emit = defineEmits(['aboutExeVisible'])/** * 办法 */// 点击确定按钮const isOk = () => {  emit('aboutExeVisible');}</script>

9. 获取子组件ref变量和defineExpose裸露

vue2中的获取子组件的ref,间接在父组件中管制子组件办法和变量的办法

父组件代码如下(示例):

<template>  <button @click="onClickSetUp">点击</button>  <Content ref="content" /></template><script setup>import {ref} from 'vue'// content组件refconst content = ref('content')// 点击设置const onClickSetUp = ({ key }) => {  content.value.modelVisible = true}</script><style scoped lang="less"></style>

子组件代码如下(示例):

<template>   <p>{{data }}</p></template><script setup>import {  reactive,  toRefs} from 'vue'/** * 数据局部* */const data = reactive({  modelVisible: false,  historyVisible: false,   reportVisible: false, })defineExpose({  ...toRefs(data),})</script>

10. 路由useRoute和useRouter的应用

<script setup>  import { useRoute, useRouter } from 'vue-router'  // 申明  const route = useRoute()  const router = useRouter()  // 获取query  console.log(route.query)  // 获取params  console.log(route.params)  // 路由跳转  router.push({    path: `/index`  })</script>

11. store仓库的应用

<script setup>  import { useStore } from 'vuex'  import { num } from '../store/index'  const store = useStore(num)  // 获取Vuex的state  console.log(store.state.number)  // 获取Vuex的getters  console.log(store.state.getNumber)  // 提交mutations  store.commit('fnName')  // 散发actions的办法  store.dispatch('fnName')</script>

12. await的反对

setup语法糖中可间接应用await,不须要写asyncsetup会主动变成async setup

<script setup>  import api from '../api/Api'  const data = await Api.getData()  console.log(data)</script>

13. provide 和 inject 祖孙传值

父组件代码如下(示例):

<template>  <AdoutExe /></template><script setup>  import { ref,provide } from 'vue'  import AdoutExe from '@/components/AdoutExeCom'  let name = ref('py')  // 应用provide  provide('provideState', {    name,    changeName: () => {      name.value = 'poetries'    }  })</script>

子组件代码如下(示例):

<script setup>  import { inject } from 'vue'  const provideState = inject('provideState')  provideState.changeName()</script>

说说你对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.extend 作用和原理

官网解释:Vue.extend 应用根底 Vue 结构器,创立一个“子类”。参数是一个蕴含组件选项的对象。

其实就是一个子类结构器 是 Vue 组件的外围 api 实现思路就是应用原型继承的办法返回了 Vue 的子类 并且利用 mergeOptions 把传入组件的 options 和父类的 options 进行了合并

  • extend是结构一个组件的语法器。而后这个组件你能够作用到Vue.component这个全局注册办法里还能够在任意vue模板里应用组件。 也能够作用到vue实例或者某个组件中的components属性中并在外部应用apple组件。
  • Vue.component你能够创立 ,也能够取组件。

相干代码如下

export default function initExtend(Vue) {  let cid = 0; //组件的惟一标识  // 创立子类继承Vue父类 便于属性扩大  Vue.extend = function (extendOptions) {    // 创立子类的构造函数 并且调用初始化办法    const Sub = function VueComponent(options) {      this._init(options); //调用Vue初始化办法    };    Sub.cid = cid++;    Sub.prototype = Object.create(this.prototype); // 子类原型指向父类    Sub.prototype.constructor = Sub; //constructor指向本人    Sub.options = mergeOptions(this.options, extendOptions); //合并本人的options和父类的options    return Sub;  };}

为什么要用 Vuex 或者 Redux

因为传参的办法对于多层嵌套的组件将会十分繁琐,并且对于兄弟组件间的状态传递无能为力。咱们常常会采纳父子组件间接援用或者通过事件来变更和同步状态的多份拷贝。以上的这些模式十分软弱,通常会导致代码无奈保护。

所以须要把组件的共享状态抽取进去,以一个全局单例模式治理。在这种模式下,组件树形成了一个微小的"视图",不论在树的哪个地位,任何组件都能获取状态或者触发行为。

另外,通过定义和隔离状态治理中的各种概念并强制恪守肯定的规定,代码将会变得更结构化且易保护。

delete和Vue.delete删除数组的区别?

  • delete只是被删除的元素变成了 empty/undefined 其余的元素的键值还是不变。
  • Vue.delete间接删除了数组 扭转了数组的键值。
var a=[1,2,3,4]var b=[1,2,3,4]delete a[0]console.log(a)  //[empty,2,3,4]this.$delete(b,0)console.log(b)  //[2,3,4]

虚构 DOM 的优缺点?

长处:

  • 保障性能上限: 框架的虚构 DOM 须要适配任何下层 API 可能产生的操作,它的一些 DOM 操作的实现必须是普适的,所以它的性能并不是最优的;然而比起粗犷的 DOM 操作性能要好很多,因而框架的虚构 DOM 至多能够保障在你不须要手动优化的状况下,仍然能够提供还不错的性能,即保障性能的上限;
  • 无需手动操作 DOM: 咱们不再须要手动去操作 DOM,只须要写好 View-Model 的代码逻辑,框架会依据虚构 DOM 和 数据双向绑定,帮咱们以可预期的形式更新视图,极大进步咱们的开发效率;
  • 跨平台: 虚构 DOM 实质上是 JavaScript 对象,而 DOM 与平台强相干,相比之下虚构 DOM 能够进行更不便地跨平台操作,例如服务器渲染、weex 开发等等。

毛病:

  • 无奈进行极致优化: 尽管虚构 DOM + 正当的优化,足以应答绝大部分利用的性能需求,但在一些性能要求极高的利用中虚构 DOM 无奈进行针对性的极致优化。

Vue 中 computed 和 watch 有什么区别?

计算属性 computed

 (1)**反对缓存**,只有依赖数据发生变化时,才会从新进行计算函数; (2)计算属性内**不反对异步操作**; (3)计算属性的函数中**都有一个 get**(默认具备,获取计算属性)**和 set**(手动增加,设置计算属性)办法; (4)计算属性是主动监听依赖值的变动,从而动静返回内容。

侦听属性 watch

 (1)**不反对缓存**,只有数据发生变化,就会执行侦听函数; (2)侦听属性内**反对异步操作**; (3)侦听属性的值**能够是一个对象,接管 handler 回调,deep,immediate 三个属性**; (3)监听是一个过程,在监听的值变动时,能够触发一个回调,并**做一些其余事件**。

extend 有什么作用

这个 API 很少用到,作用是扩大组件生成一个结构器,通常会与 $mount 一起应用。

// 创立组件结构器let Component = Vue.extend({ template: "<div>test</div>" });// 挂载到 #app 上new Component().$mount('#app')// 除了下面的形式,还能够用来扩大已有的组件let SuperComponent = Vue.extend(Component);new SuperComponent({  created() {    console.log(1);  },});new SuperComponent().$mount("#app");

你感觉vuex有什么毛病

剖析

相较于reduxvuex曾经相当简便好用了。但模块的应用比拟繁琐,对ts反对也不好。

体验

应用模块:用起来比拟繁琐,应用模式也不对立,基本上得不到类型零碎的任何反对

const store = createStore({  modules: {    a: moduleA  }})store.state.a // -> 要带上 moduleA 的key,内嵌模块的话会很长,不得不配合mapState应用store.getters.c // -> moduleA里的getters,没有namespaced时又变成了全局的store.getters['a/c'] // -> 有namespaced时要加path,应用模式又和state不一样store.commit('d') // -> 没有namespaced时变成了全局的,能同时触发多个子模块中同名mutationstore.commit('a/d') // -> 有namespaced时要加path,配合mapMutations应用感觉也没简化

答复范例

  1. vuex利用响应式,应用起来曾经相当方便快捷了。然而在应用过程中感觉模块化这一块做的过于简单,用的时候容易出错,还要常常查看文档
  2. 比方:拜访state时要带上模块key,内嵌模块的话会很长,不得不配合mapState应用,加不加namespaced区别也很大,gettersmutationsactions这些默认是全局,加上之后必须用字符串类型的path来匹配,应用模式不对立,容易出错;对ts的反对也不敌对,在应用模块时没有代码提醒。
  3. 之前Vue2我的项目中用过vuex-module-decorators的解决方案,尽管类型反对上有所改善,但又要学一套新货色,减少了学习老本。pinia呈现之后应用体验好了很多,Vue3 + pinia会是更好的组合

原理

上面咱们来看看vuexstore.state.x.y这种嵌套的门路是怎么搞进去的

首先是子模块装置过程:父模块状态parentState下面设置了子模块名称moduleName,值为以后模块state对象。放在下面的例子中相当于:store.state['x'] = moduleX.state。此过程是递归的,那么store.state.x.y装置时就是:store.state['x']['y'] = moduleY.state
//源码地位 https://github1s.com/vuejs/vuex/blob/HEAD/src/store-util.js#L102-L115if (!isRoot && !hot) {    // 获取父模块state    const parentState = getNestedState(rootState, path.slice(0, -1))    // 获取子模块名称    const moduleName = path[path.length - 1]    store._withCommit(() => {        // 把子模块state设置到父模块上        parentState[moduleName] = module.state    })}

请说出vue cli我的项目中src目录每个文件夹和文件的用法

  • assets文件夹是放动态资源;
  • components是放组件;
  • router是定义路由相干的配置;
  • view视图;
  • app.vue是一个利用主组件;
  • main.js是入口文件

Vue computed 实现

  • 建设与其余属性(如:dataStore)的分割;
  • 属性扭转后,告诉计算属性从新计算
实现时,次要如下
  • 初始化 data, 应用 Object.defineProperty 把这些属性全副转为 getter/setter
  • 初始化 computed, 遍历 computed 里的每个属性,每个 computed 属性都是一个 watch 实例。每个属性提供的函数作为属性的 getter,应用 Object.defineProperty 转化。
  • Object.defineProperty getter 依赖收集。用于依赖发生变化时,触发属性从新计算。
  • 若呈现以后 computed 计算属性嵌套其余 computed 计算属性时,先进行其余的依赖收集

Vue-router 导航守卫有哪些

  • 全局前置/钩子:beforeEach、beforeResolve、afterEach
  • 路由独享的守卫:beforeEnter
  • 组件内的守卫:beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave

如何从实在DOM到虚构DOM

波及到Vue中的模板编译原理,次要过程:

  1. 将模板转换成 ast 树, ast 用对象来形容实在的JS语法(将实在DOM转换成虚构DOM)
  2. 优化树
  3. ast 树生成代码