vue相关题目汇总

一、v-show 和 v-if 区别?

  1. v-show 通过 css display 控制显示与隐藏
  2. v-if 组件真正的渲染和销毁,而不是显示与隐藏
  3. 频繁切换显示状态用 v-show,否则用 v-if

二、为何在 v-for 中用 key?

  1. 为了提升渲染性能(减少渲染次数)
  2. key 最好避免是 rendom index 索引这种
  3. 在 diff 算法中,对比新旧 vnode 时 (children 节点),根据元素 target 和 key 判断是否是 sameNode, 是否更新渲染
    // 通过 diff 算法中进行 patch 打补丁时, 对比 children 节点时 通过 tag 和 key 来判断,是否是 sameNode(相同节点)

三、描述 Vue 组件生命周期(父子组件)?

  1. 单组件生命周期(11 个生命周期钩子)
/* 生命周期 */
// 1. 生命周期就是组件实例出来到销毁的一系列过程。分为挂载阶段、更新阶段、销毁阶段。
// 2.
// 挂载阶段 -- 挂载阶段主要是组件的实例化,然后模板解析 然后render生成vnode节点(这时候已经实现响应式了),最后替换页面指定el(挂载到页面el上),第一次渲染完成
// beforeCreate
// created,        --初始化data数据(实现响应式)
// beforeMount     --在挂载开始之前被调用
// mounted         --vnode节点挂载到页面空el上【patch(ele, vnode)】,渲染DOM完成
// --官方说法:el 被新创建的 vm.$el(vue实例) 替换,并挂载到实例上去之后调用该钩子。

// 更新阶段 -- 更新阶段就是,data变化触发响应式setter方法,重新render生成vnode节点,通过diff算法对比新旧vnode节点,进行patch打补丁,更新渲染完成
// beforeUpdate    --这时还没生成vnode节点; 可以修改状态不会触发二次渲染。(react 中这里setState设置容易出现死循环)
// updated         --这时已经生成vnode节点,通过diff算法对比新旧vnode,进行patch打补丁【patch(vnode, newVnode)】,重新更新渲染DOM

// 卸载阶段
// beforeDestory  --销毁前;一般销毁自定义事件、DOM事件
// destoryed

// 其他钩子
// activated(活跃状态)     --keep-alive 当前组件显示时执行的生命周期。
// deactivated(缓存状态)   --keep-alive 当前组件缓存时执行的生命周期。
// errorCaptured          --当捕获一个来自子孙组件的错误时被调用

/* */
// Has 'el' option?
//    --如果在实例化vue时指定el,则该vue将会渲染在el对应的DOM中
//    --反之 没有指定el,则vue实例会处于一种“未挂载”的状态,此时通过vm.$mount来手动执行挂载
  1. 父子组件生命周期关系(created 和 mounted 关系,update 关系,destory 关系)
// 1.挂载阶段 --组件第一次渲染(创建js模型 ==> 渲染)
    父beforeCreate ==> 父created ==> 父beforeMount
    ==> 子beforeCreate==> 子created  ==> 子beforeMount ==> 子mounted
    ==> 父mounted
// 2.更新阶段
    父beforeUpdate ==> 子beforeUpdate ==> 子updated ==> 父updated
// 3.卸载阶段
    父beforeDestroy ==> 子beforeDestroy ==> 子destroyed ==> 父destroyed

四、vue 组件如何通讯(常见)?

  1. 父子组件 props 和 this.$emit
  2. 自定义事件event.$on 、event.$off 、event.$emit
// - 实例化Vue (vue就可以支持自定义事件)
import Vue from "vue";
export default new Vue();
// - 绑定自定义事件
event.$on("onAddTitle", this.addTitleHandler);
// - 调用自定义事件
event.$emit("onAddTitle", this.title);
// - 及时销毁,否则可能造成内存泄露
event.$off("onAddTitle", this.addTitleHandler);
  1. vuex

五、描述组件渲染和更新的过程?

  • 简化说
  1. 初次渲染的过程
    模板解析成 render 函数, 然后执行 render 生成 vnode 节点(这时候已经实现响应式了),最后挂载到页面空 el 上,第一次渲染完成
    // 解析模板为 render 函数–>触发响应式–>执行 render 函数,生成 vnode–>执行 patch(ele, vnode),渲染 DOM
  2. 更新过程
    data 变化触发响应式,重新 render 生成 vnode,通过 diff 算法对比新旧 vnode 节点,进行 patch 打补丁,重新更新渲染
    // data 变化,触发 setter(在 getter 已被监听的)–>render 函数,生成 newVnode–>执行 patch(vnode, newVnode),更新 dom
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BFWoV1Mc-1600405704198)(./img/update.jpg)]
  • 照图说
    dep: 主要是用来在数据更新的时候通知 watchers 进行更新;
    watcher 接受到通知之后,会通过回调函数进行更新。
  1. 解析模板为 render 函数 -> 触发响应式(监听 data 属性的 getter 的依赖收集,也即是往 dep 里面添加 watcher 的过程)
    -> 执行 render 函数 -> 渲染 DOM
  2. 修改 data,setter(必需是初始渲染已经依赖过的)调用 Dep.notify(),将通知它内部的所有的 Watcher 对象进行视图更新
    -> 重新执行 render 函数-> 更新 DOm

六、双向数据绑定 v-model 的实现原理?

  1. v-model 语法糖
  2. 如 input 元素 给 value 设置 this.name
  3. 绑定 input 事件 this.name = $event.target.value
  4. data 更新触发 re-render

七、MVVM 的理解?

  1. 一种数据驱动视图思想
  2. M:Model(数据层) V:View(视图层) ViewModel(好比两者之前的桥梁,做关联)
    数据变化会 通过 VM 更新视图
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-B55Xz3N1-1600405704203)(./img/mvvm.jpg)]

八、computed 有何特点?

  1. 计算属性,data 里的数据依赖变化会重新计算,如果没变化会读取缓存
  2. 提高性能

九、为何组件 data 必须是一个函数?

  1. 因为函数可以构成作用域,避免每个组件的 data 数据共享;
  2. 如果 data 是个对象,导致所有 vue 的实例(也就是所有组件的实例)可以共享一份数据(一个变了全部变),可以间接改变其他组件的 data(原型继承–指向相同,所有实例都会改变)

十、ajax 请求应该放在那个生命周期?

  1. 为了保证逻辑统一 一般放在 mounted
  2. mounted 可以获取 dom 节点操作,created Dom 还没渲染,一般保证逻辑统一性 放 mounted 里
  3. 当也会有人放在 created 里,无法操作 DOM, 零外 created 也就比 mounted 早调用几微秒而已,性能没啥提高

十一、如何将组件所有 props 传递给子组件?

细节知识点

  1. $props
  2. <User v-bind="$props"/>
    l

十二、如何自己实现 v-model?

// 1.单选复选框事件change事件 checked,输入框input事件 value
// 2.父组件( 父组件 直接加v-model)
    <CustomVModel v-model="name"/> //自定义 v-model

// 3.子组件(v-modle语法糖)
//  比如给input,  value设置变量  这个值对应(moldel里的props值 还有和props接受的值一致)
//  绑定input事件 让它$emit调用事件,($event.target.value)   这个事件名对应(moldel里对应event值)
    <input type="text"
        :value="text1"
        @input="$emit('change1', $event.target.value)"
    />
    // v-modle语法糖
    export default {
        model: {
            prop: 'text1', // 对应 props text1
            event: 'change1'
        },
        props: {
            text1: String,
            default() {
                return ''
            }
        }
    }

十三、多个组件有相同逻辑,如何抽离?

  1. mixin(js 文件 写法和 vue script 里一致)
  2. mixin 缺点
// 1. 多个mixin可能会造成命名冲突
// 2. 变量来源不明确,不利于阅读
// 3. mixin和组件可能出现多对多的关系,复杂度较高
// 4. vue3.0提出的Composition API(组合)旨在解决这些问题
import myMixin from "./mixin";
export default {
  mixins: [myMixin] // 可以添加多个,会自动合并起来
};

十四、何时使用异步组件?

  1. 有时页面需要加载的内容比较多,卡顿不利于用户体验(类似首屏加载),可以对大组件懒加载(编辑器、图表)
  2. 路由懒加载(切换路由)

十五、何时使用 keep-alive?

  1. 需要缓存的组件(不需要再重复渲染)(如:展示的 tab 页面)
  2. 性能优化
// 1.在动态组件中的应用
<keep-alive :include="whiteList" :exclude="blackList" :max="amount">
     <component :is="currentComponent"></component>
</keep-alive>

// 2.在vue-router中的应用
// 利用meta属性
<keep-alive>
  <router-view v-if="this.$route.meat.keepAlive"></router-view> // 这里是会被缓存的组件
</keep-alive>
<router-view v-if="!$route.meta.keepAlive"></router-view>

// 3.include定义缓存白名单(name属性)  exclude定义缓存黑名单  max定义缓存组件上限
// 4.钩子 activated  deactivated
// 5.keepAlive缓存的组件:
// 第一次进入:
// beforeMount -> mounted -> actived
// 后续进入时:
// deactivated -> activated -> deactivated
// 组件被销毁/离开路由:
// beforeRouteLeave-> 全局beforeEach -> 全局afterEach -> deactived

// 组件销毁时deactived可以替换beforeDestory做一些事

十六、何时使用 beforeDestory?

  1. 解绑自定义事件 event.$off
  2. 清除定时器
  3. 解绑自定义 DOM 事件(addEventListener),如 window scroll 等

十七、什么是插槽?

// 1. 插槽:是vue一种内容分发机制, 可以理解为一个占位符
//        将slot元素作为 承载分发内容的出口(Vue实现的一套内容分发的API)

// 2. 作用域插槽 --插槽的一种(slot元素里定义一个data数据,然后v-slot接受使用)
//定义:
        <slot :d="msg" :flag="true">默认值!!!</slot>

        <slot :slotData="website">
            {{website.subTitle}}
        </slot>
        const website= {subTitle:'xxxx', title: 'yyy'} // 数据源
// 使用:
        <div slot-scope="scope">
            {{ scope.d }} {{ scope.flag }}
        </div>

        <template v-slot="slotProps">
            {{slotProps.slotData.title}}
        </template>

// 2.普通插槽 (slot元素作为占位符,使用时替换slot元素)
// 定义:
        <slot>默认值!!!</slot>
// 使用:
        <msg-box>
          // 传递过去的内容
        	<h2>您是否确定删除?</h2>
      	</msg-box>
// 3.具名插槽 (相对于普通插槽 slot元素设置了name属性,使用时用v-slot标明哪个name属性)
// 定义:
        <slot name="title"> </slot>
// 使用:
        <div slot="title"> xxx </div>
        <template v-slot="title">
            xxx
        </template>

十八、介绍 vuex?

/* 1. 介绍vuex */
// 是基于vue的一个状态管理工具,主要解决组件间状态共享的问题

/* 2.几个核心概念 */
// 1. store 容器,存储状态的
// 2. state 定义共享的状态变量,vuex store实例的状态对象
// 3. getter 读取器,相当于store的计算属性
// 4. mutation 修改器,用于修改state的数据
//           --state的数据只能通过mutations中的方法修改
// 5. action 方法,做异步操作,内部可以整合多个mutations方法
// 6. module 模块化, 可以将state模块化(nameSpaced 命名空间)

/* 3.action和mutation有何区别 */
// 1.action里要处理异步,mutation不可以
// 2.mutation 做修改store数据的操作
// 3.action可以整合多个mutation

//---------------------------------------------------------------
/* 4.为什么vuex mutation 和 redux reducer中不能做异步操作 */
// 异步操作的话,没法知道状态是何时更新(异步回调),调试带来困难,无法很好地进行状态追踪(devtools)

十九、Vue-router 常用的路由模式?

// 1. hash模式(默认)、H5 history模式
// 2. hash特点:
//    hash变化会触发网页前进后退
//    hash变化不会刷新页面(SPA单页面)
//    hash永远不会提交到server端
// 3. H5 history
//    用url规范的路由,但跳转时不刷新页面
//    依赖服务端 返回index.html格式的路由
//    通过设置history.pushState,打开新路由 页面不会刷新
//    通过设置window.onpopstate,回调监听浏览器前进后退
// 4. 两者区别
//    形式上/#/
//    是否依赖服务端配置

// 5. 两者选择
// 看对url规范敏感不,不敏感可以用hash(B系统),C系统可以考虑H5 history

/* H5 history */
// 页面初次加载,获取 path
document.addEventListener("DOMContentLoaded", () => {
  console.log("load", location.pathname);
});

// 打开一个新的路由,浏览器不会刷新页面
// 【注意】用 pushState 方式
document.getElementById("btn1").addEventListener("click", () => {
  const state = { name: "page1" };
  console.log("切换路由到", "page1");

  /* history.pushState */
  history.pushState(state, "", "page1"); // http://127.0.0.1:8001/page1
});

// 监听浏览器前进、后退
/*  window.onpopstate */
window.onpopstate = event => {
  console.log("onpopstate", event.state, location.pathname); // location.pathname: /xxx.html
};

二十、如何配置 Vue-router 异步加载?

  1. import
// 1. 路由懒加载/异步加载
// () => import(/* webpackChunkName: "dashboard" */'@/views/dashboard/index')

// 2. 路由配置细节
import Router from 'vue-router'
export const constantRouterMap = [
  {
    path: '/',
    component: Layout,
    redirect: '/dashboard',
    children: [
      {
        path: 'dashboard',
        component: () => import(/* webpackChunkName: "dashboard" */'@/views/dashboard/index'),
        name: 'Dashboard',
        meta: {
          title: 'dashboard',
          icon: 'dashboard',
      }
    ]
  },

]
const createRouter = () => new Router({
  // mode: 'history', // require service support
  routes: constantRouterMap
})
const router = createRouter()
router.$addRoutes = (params) => { // 解决路由表重复
  router.matcher = createRouter().matcher // 重置 router
  router.addRoutes(params)
}
export default router

二十一、请用 vnode 描述一个 DOM 结构?

// 1. htmldemo
<div id="div1" class="container">
  <p>vdom</p>
  <ul style="font-size:20px;">
    <li>a</li>
  </ul>
</div>;

// 2. vnode模拟DOM(tag/target/sel, props/data, text, children[])
//    tag/target/sel、props/data 写法自定义
const vdom = {
  tag: "div",
  props: {
    className: "container",
    id: "div1"
  },
  children: [
    { tag: "p", children: "vdom" },
    {
      tag: "ul",
      props: { style: "font-size: 20px;" },
      children: [
        { tag: "li", children: "a" }
        // ....
      ]
    }
  ]
};

二十二、描述下 vue 的响应式?

/* 1. 响应式实现原理 */
// 1.原理是拦截data的getter/setting方法(vue2.——通过Object.defineProperty, vue3.0——通过proxy)
// 2.监听getter方法,data变化会 通知setter方法 然后更新渲染视图
//      发布订阅者模式,getter方法里里订阅,setter方法里发布通知,让所有订阅者完成响应
//      https://segmentfault.com/a/1190000023949535

/* 2. vue2.x和vue3.0实现响应式不同点 */
// 1.Proxy能解决Object.defineProperty的问题(原生监听数组、新增、删除属性)
// 2.Proxy无法兼容所有浏览器,无法polyfill(ie11)
// 3.Proxy实现响应式 深度监听性能更好(get获取的时候,深度递归),vue2.x通过一次性深度递归,消耗性能

/* 3. vue监听数组变化 */
// 1. Proxy可以原生支持监听数组的变化(vue3.0)
// 2. Object.defineProperty不能原生监听数组变化, 需要重新定义数组原型 重写push pop...方法 实现监听
//      重新Object.create 一个Array原型, 然后重写这个新Array原型的push,pop方法,最后让监听的数组的原型 指向 新Array原型
const oldArrayProperty = Array.prototype;
const arrProto = Object.create(oldArrayProperty) // Object.create(Array.prototype)
  [
    // 重写push pop...方法
    ("push", "pop", "shift", "unshift", "splice")
  ].forEach(methodName => {
    // arrProto.push = function(){ oldArrayProperty.push.call(this, ...arguments)}
    arrProto[methodName] = function () {
      updateView(); // 触发视图更新
      oldArrayProperty[methodName].call(this, ...arguments);
      // Array.prototype.push.call(this, ...arguments)
    };
  });

// 判断数组
if (Array.isArray(target)) {
  target.__proto__ = arrProto; // 原型指向arrProto
}

/* 4. vue2.x 实现深度监听及监听数组 */
// 触发更新视图
function updateView() {
  console.log("视图更新");
}

/* 重新定义数组原型 */
const oldArrayProperty = Array.prototype;
const arrProto = Object.create(oldArrayProperty);
// 创建新对象,原型指向 oldArrayProperty ,再扩展新的方法不会影响原型
["push", "pop", "shift", "unshift", "splice"].forEach(methodName => {
  // arrProto.push = function(){ oldArrayProperty.push.call(this, ...arguments)}
  arrProto[methodName] = function () {
    updateView(); // 触发视图更新
    oldArrayProperty[methodName].call(this, ...arguments);
    // Array.prototype.push.call(this, ...arguments)
  };
});

/* 重新定义属性,监听起来 */
function defineReactive(target, key, value) {
  /* 深度监听 */
  observer(value);
  // 数组不走这个方法
  Object.defineProperty(target, key, {
    get() {
      return value;
    },
    set(newValue) {
      if (newValue !== value) {
        /* 深度监听 */
        observer(newValue); // 防止 data.xxx = {name: 12} ==> data.xxx.name = 122
        value = newValue; // 设置新值
        // 注意,value 一直在闭包中,此处设置完之后,再 get 时也是会获取最新的值
        updateView(); // 触发更新视图
      }
    }
  });
}
/* 监听对象属性 */
function observer(target) {
  // 非对象或数组
  if (typeof target !== "object" || target === null) {
    return target;
  }
  // 污染全局的 Array 原型
  // Array.prototype.push = function () {
  //     updateView()
  //     ...
  // }
  // 判断数组
  if (Array.isArray(target)) {
    target.__proto__ = arrProto; // 原型指向arrProto
  }
  // 重新定义各个属性(for in 也可以遍历数组)
  for (let key in target) {
    defineReactive(target, key, target[key]);
  }
}
// 准备数据
const data = {
  name: "zhangsan",
  age: 20,
  info: {
    address: "北京" // 需要深度监听
  },
  formData: {
    lists: [
      {
        value: 123
      }
    ]
  },
  nums: [10, 20, 30]
};
// 监听数据
observer(data);

// 测试
// data.name = 'lisi'
// data.age = 21
// // console.log('age', data.age)
// data.x = '100' // 新增属性,监听不到 —— 所以有 Vue.set
// delete data.name // 删除属性,监听不到 —— 所有已 Vue.delete
// data.info.address = '上海' // 深度监听

// data.nums.push(4) // 监听数组

/* 5.vue3.0实现深度监听、数组 */
// 创建响应式
function reactive(target = {}) {
  if (typeof target !== "object" || target == null) {
    // 不是对象或数组,则返回
    return target;
  }
  // 代理配置
  const proxyConf = {
    get(target, key, receiver) {
      // 只处理本身(非原型的)属性
      const ownKeys = Reflect.ownKeys(target);
      if (ownKeys.includes(key)) {
        console.log("get", key); // 监听
      }

      const result = Reflect.get(target, key, receiver);

      // 深度监听
      // 性能如何提升的?(通过get获取的时候,深度递归, vue2.x通过一次性深度递归)
      return reactive(result);
      // return result
    },
    set(target, key, val, receiver) {
      // 重复的数据,不处理
      if (val === target[key]) {
        return true;
      }

      const ownKeys = Reflect.ownKeys(target);
      if (ownKeys.includes(key)) {
        console.log("已有-key", key);
      } else {
        // 新增的属性
        console.log("新增-key", key);
      }

      const result = Reflect.set(target, key, val, receiver);
      console.log("set", key, val);
      // console.log('result', result) // true
      return result; // 是否设置成功
    },
    deleteProperty(target, key) {
      const result = Reflect.deleteProperty(target, key);
      console.log("delete-property", key);
      // console.log('result', result) // true
      return result; // 是否删除成功
    }
  };
  // 生成代理对象
  const observed = new Proxy(target, proxyConf);
  return observed;
}

// 测试数据
const data = {
  name: "zhangsan",
  age: 20,
  info: {
    city: "beijing",
    a: {
      b: {
        c: {
          d: {
            e: 100
          }
        }
      }
    }
  }
};
const list = [10, 11, 12];

const proxyData1 = reactive(data);
const proxyData2 = reactive(list);

proxyData1.info.city;
/* 打印结果 */
// get info
// get city

proxyData1.info.a.add = "嵌套新增属性";
/* 打印结果 */
// get a
// 新增-key add
// set add 嵌套新增属性

proxyData1.add = "新增属性";
/* 打印结果 */
// 新增-key add
// set add 新增属性

proxyData2.shift(); // 删除首位
/* 打印结果 */
// get length
// get 0
// get 1  // 1->0
// 已有-key 0
// set 0 11
// get 2  // 2->1
// 已有-key 1
// set 1 12
// delete-property 2 // delete 2
// 已有-key length
// set length 2

二十三、diff 算法?

/* 1. diff算法时间复杂度 */
// 1.在vue和react中运用到了diff算法,对比新旧vnode
// 2.优化了时间复杂度O(n),在O(n^3)基础上做了调整(只比较同一层级,target不相同直接删掉重建,target相同对比下一级children)

/* 2. diff算法对比vnode过程 */
// 1.对比新旧vnode变化 只比较同一层级,不跨级比较
// 2.如果target不相同,会删掉 重建(不再深度比较)
// 3.如果target相同,会对比下一级children(如果一个有值一个没值,就对应添加和删除)
// 4.根据target和key是否相同,来判断是否是相同节点,不是相同节点 继续深度比较
// -----------------------------------------------------------------------------
//     patch(elem,vnode)和patch(vnode,newVnode)
//     patch: 对比vnode的变化 更新DOM
//     patch的两种用法(把vnode渲染到空的ele上、新的vnode更新旧的)

二十四、Vue 为何是异步渲染,$nextTick 何用?

  1. vue 异步执行 DOM 更新(异步渲染)可以提高渲染性能,整合 data 修改 一次性渲染
  2. $nextTick DOM 更新完成之后,立即触发回调

二十五、Vue 常见性能优化方法?

  1. 合理使用 v-show 和 v-if,频繁切换用 v-show
  2. 需要组件之间来回切换的一些场景(展示静态展示 tab 数据),使用 keep-alive 取缓存,减少渲染次数
  3. 定义 data 数据时,层级控制不要太深(除非像 tree 的数据),减少深度比较
  4. v-for 时加 key 避免取 random index 索引,可以减少渲染次数 避免与 v-if 使用
  5. DOM 事件、自定义事件 组件销毁时需要解绑
  6. 页面加载内容多 卡顿影响用户体验,可以进行组件懒加载,提升渲染性能(如:优化首屏加载)
    //在首屏渲染、用户交互的过程中,要巧用同步环境及异步环境;首屏展现的内容,尽量保证在同步环境中完成;其他内容,拆分到异步中,从而保证性能、体验。
  7. 路由里面也进行懒加载
  8. webpack 层面的优化
// 1. 小图片可以base64编码  babel-loader 开始缓存
    module: {
        rules: [
            // js
            {
                test: /\.js$/,
                loader: ['babel-loader?cacheDirectory'], // 开启缓存
                include: srcPath,
                // exclude: /node_modules/
            },
            {
                test: /\.(png|jpg|jpeg|gif)$/,
                use: {
                    loader: 'url-loader',
                    options: {
                        limit: 5 * 1024,
                        // 小于 5kb 的图片用 base64 格式产出,
                        // 否则,依然延用 file-loader 的形式,产出 url 格式
                        outputPath: '/img1/',
                        // >=5kb 打包到配置出口根目录的 img1/
                        // 打包到 img1 目录下

                        // 设置图片的 cdn 地址(也可以统一在外面的 output 中设置,那将作用于所有静态资源)
                        // publicPath: 'http://cdn.abc.com'
                    }
                }
            }
        ]
    }

// 2.打包js文件时bundle加hash(提高加载效率,js代码没更新 可以取缓存)
    output: {
        // filename: 'bundle.[contentHash:8].js',  // 打包代码时,加上 hash 戳
        filename: '[name].[contentHash:8].js', // name 即多入口时 entry 的 key
        path: distPath,

        // publicPath: 'http://cdn.abc.com'
        // 修改所有静态文件 url 的前缀(如 cdn 域名),这里暂时用不到
    },

// 3. 抽离css文件时加hash
    const MiniCssExtractPlugin = require("mini-css-extract-plugin")
    new MiniCssExtractPlugin({
        // filename: 'css/main.[contentHash:8].css'
        filename: 'css/[name].[contentHash:8].css'
    })

// 4. 打包文件过大,分割代码 抽离js文件(splitChunk、 htmlWebpackPlugin引用)
    plugins:[
    new HtmlWebpackPlugin({
        template: path.join(srcPath, 'index.html'),
        filename: 'index.html',
        // chunks 表示该页面要引用哪些 chunk (即上面的 index 和 other),默认全部引用
        chunks: ['chunk-vendors', 'chunk-elementUI', 'index']  // 要考虑代码分割
    }),
    ],
    optimization:{
        splitChunks: {
            /**
            * initial 入口chunk,对于异步导入的文件不处理
                async 异步chunk,只对异步导入的文件处理
                all 全部chunk
            */
            chunks: "all",
            // 缓存分组
            cacheGroups: {
                vendors: {
                name: "chunk-vendors",
                test: /[\\\/]node_modules[\\\/]/,
                priority: 10,
                chunks: "initial"// 只打包初始时依赖的第三方
                },
                elementUI: {
                name: "chunk-elementUI",// 单独将 elementUI 拆包
                priority: 20,// 权重要大于 libs 和 app 不然会被打包进 libs 或者 app
                test: /[\\\/]node_modules[\\\/]element-ui[\\\/]/
                },
                echarts: {
                name: "chunk-echarts",// 单独将 elementUI 拆包
                priority: 20,// 权重要大于 libs 和 app 不然会被打包进 libs 或者 app
                test: /[\\\/]node_modules[\\\/]echarts[\\\/]/
                },
                commons: {
                name: "chunk-comomns",
                // test: resolve("src/components"),// 可自定义拓展你的规则
                minChunks: 2,// 最小共用次数
                priority: 5,
                reuseExistingChunk: true
                }
            }
        }
    }

// 5. 避免引入无用模块(ingorePlugin  moment)
new webpack.IgnorePlugin(/\.\/locale/, /moment/),

// 5. 压缩代码 去console debugger
   const TerserJSPlugin = require('terser-webpack-plugin')
   const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
    optimization: {
        minimizer: [
             new TerserJSPlugin({ // js压缩
                // cache: true,
                parallel: true,
                sourceMap: false, // Must be set to true if using source-maps in production
                terserOptions: {
                    compress: {
                    warnings: false,
                    drop_console: true,
                    drop_debugger: true,
                    },
                },
            }),
            new OptimizeCSSAssetsPlugin({})] // css压缩
    }

// 6. 使用CDN加速(设置静态资源)
    output: { // 设置所有所有静态资源cdn
        // filename: 'bundle.[contentHash:8].js',
        filename: '[name].[contentHash:8].js',
        path: distPath,
        publicPath: 'http://cdn.abc.com'
        // 修改所有静态文件 url 的前缀(如 cdn 域名),这里暂时用不到
    },
    module: {
        rules: [
            { // 设置部分静态资源cdn(png jpg jpeg gif 图片)
            test: /\.(png|jpg|jpeg|gif)$/,
            use: {
                loader: 'url-loader',
                options: {
                    limit: 5 * 1024,
                    outputPath: '/img1/', // >=5kb 打包到配置出口根目录的 img1/   // 打包到 img1 目录下
                    publicPath: 'http://cdn.abc.com'
                    // 设置图片的 cdn 地址(也可以统一在外面的 output 中设置,那将作用于所有静态资源)
                }
            }
            }
        ]
    }

// 合理使用 computed
// 使用 vue-loader 在开发环境做模板预编译(预编译)
// 前端通用的性能优化,如图片懒加载
// 使用 SSR

二十六、vuex 和 redux 区别?

// 1. vuex借鉴了redux,在这基础上进行修改 简化了一些流程,对状态管理更加明确(副作用 基于vue)
// 2. 修改state方式: vuex, 同步修改state 在mutation中修改, 异步修改state,通过action 再整合mutation修改
// 3. redux,无论同步还是异步修改state,都必须通过action 再通知reducer
// 4. vuex自带getter这种衍生属性
// ---------------------------------------------------------------------------------------------------
// 5. vuex是基于vue的,响应式 有自动渲染的功能,所以不需要手动更新,redux subscribe监听store变化,通过store.getState()方法获取最新状态 再触发

二十七、vue 和 react 区别?

// - 共同点
// 1. 都支持组件化
// 2. 都是数据驱动视图
// 3. 都是用vdom操作DOM

// - 不同点
// 1. React使用jsx 拥抱js, Vue使用模板编译 拥抱html
// 2. React函数式编程( setState 传入state 返回jsx), Vue声明式编程(声明变量, data.a data.b 操作)
// 3. vue有很多衍生属性(watch computed 指令),react没有需要自力更生

二十八、vue2.x 和 vue3.0 区别?

// 1. 用ts重写(响应式、vdom、模板编译等)
// 2. 响应式实现方式不一样(Proxy存在浏览器兼容性问题,且不能polyfill)
// 3. 提升性能,打包体积减少
// 4. 新增composition api( 围绕一个新的组件选项setup而创建。setup() 为 Vue 组件提供了状态、计算值 和生命周期钩子)
/*  
    2-0.生命周期钩子改变了
        使用setup() 使用setup() onBeforeMount onMounted onBeforeUpdate onUpdated
        onBeforeUnmount onUnmounted
    2-1. reactive 生命 创建响应式对象,非包装对象 --没有包装属性value(类似vue2.x data中声明的变量)
         <h1>{{title.name}}</h1>

         import { reactive } from 'vue'
         setup(){
             const title = reactive({
                 name:'xxxxxx
             })
             return { title }
         }

    2-2. ref 声明 创建一个包装式对象 --含有一个响应式属性value 
         <h1>{{title.name}}</h1>
         <h1>{{user}}</h1>

         import { reactive, ref } from 'vue'
         setup(){
             const title = reactive({
                 name:'xxxxxx
             })
             const user = ref('yyyy')
             user.value = 'YYYYY'  // 修改值 通过value
             return { title, user }
         }
    2-3. computed变化  生命周期钩子onMounted
    
         <div>当前count:{{ computedCount }}</div>
         <button @click="increment">修改count</button>

         import { reactive, ref, computed } from 'vue'
         setup(){
             const count = ref(0)
             const increment = ()=>{
                 count.value ++
             }

             // 生命周期
             onMounted(()=>{
                 console.log('mounted')
             })

             const computedCount = computed(()=> count.value*10 )
             return { count, increment, computedCount }
         }
    // ----------------------------------------------------------------------
    2-4. 事件处理
    
*/

二十九、vue3.0 composition api 与 reatct hooks 区别?

// 1. Vue Composition API:围绕一个新的组件选项setup而创建。setup() 为 Vue 组件提供了状态、计算值 和生命周期钩子
// 2. react hooks 纯函数组件、无副作用( hooks加入了状态,useEffect里执行副作用代码)
// 3. 代码的执行
// vue setup() 在created 钩子之前被调用(beforeCreate之后)
// Vue setup() 只在组件创建时 调用
// React hooks 组件渲染时候就会 调用(render方法必须遵守一些规则)
// hooks只能出现在函数作用域的顶级,不能出现在条件语句、循环语句中、嵌套函数中。
// ------------------------------------------------------------------------------------
// 4. 声明状态
// react用useState 声明状态
// vue 用 reactive ref声明状态
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值