Computed 和 Methods 的区别

能够将同一函数定义为一个 method 或者一个计算属性。对于最终的后果,两种形式是雷同的

不同点:

  • computed: 计算属性是基于它们的依赖进行缓存的,只有在它的相干依赖产生扭转时才会从新求值;
  • method 调用总会执行该函数。

如何定义动静路由?如何获取传过来的动静参数?

(1)param形式

  • 配置路由格局:/router/:id
  • 传递的形式:在path前面跟上对应的值
  • 传递后造成的门路:/router/123

1)路由定义

//在APP.vue中<router-link :to="'/user/'+userId" replace>用户</router-link>    //在index.js{   path: '/user/:userid',   component: User,},

2)路由跳转

// 办法1:<router-link :to="{ name: 'users', params: { uname: wade }}">按钮</router-link// 办法2:this.$router.push({name:'users',params:{uname:wade}})// 办法3:this.$router.push('/user/' + wade)

3)参数获取
通过 $route.params.userid 获取传递的值

(2)query形式

  • 配置路由格局:/router,也就是一般配置
  • 传递的形式:对象中应用query的key作为传递形式
  • 传递后造成的门路:/route?id=123

1)路由定义

//形式1:间接在router-link 标签上以对象的模式<router-link :to="{path:'/profile',query:{name:'why',age:28,height:188}}">档案</router-link>// 形式2:写成按钮以点击事件模式<button @click='profileClick'>我的</button>    profileClick(){  this.$router.push({    path: "/profile",    query: {        name: "kobi",        age: "28",        height: 198    }  });}

2)跳转办法

// 办法1:<router-link :to="{ name: 'users', query: { uname: james }}">按钮</router-link>// 办法2:this.$router.push({ name: 'users', query:{ uname:james }})// 办法3:<router-link :to="{ path: '/user', query: { uname:james }}">按钮</router-link>// 办法4:this.$router.push({ path: '/user', query:{ uname:james }})// 办法5:this.$router.push('/user?uname=' + jsmes)

3)获取参数

通过$route.query 获取传递的值

Class 与 Style 如何动静绑定

Class 能够通过对象语法和数组语法进行动静绑定

对象语法:

<div v-bind:class="{ active: isActive, 'text-danger': hasError }"></div>data: {  isActive: true,  hasError: false}

数组语法:

<div v-bind:class="[isActive ? activeClass : '', errorClass]"></div>data: {  activeClass: 'active',  errorClass: 'text-danger'}

Style 也能够通过对象语法和数组语法进行动静绑定

对象语法:

<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>data: {  activeColor: 'red',  fontSize: 30}

数组语法:

<div v-bind:style="[styleColor, styleSize]"></div>data: {  styleColor: {     color: 'red'   },  styleSize:{     fontSize:'23px'  }}

ref和reactive异同

这是Vue3数据响应式中十分重要的两个概念,跟咱们写代码关系也很大

const count = ref(0)console.log(count.value) // 0count.value++console.log(count.value) // 1const obj = reactive({ count: 0 })obj.count++
  • ref接管外部值(inner value)返回响应式Ref对象,reactive返回响应式代理对象
  • 从定义上看ref通常用于解决单值的响应式,reactive用于解决对象类型的数据响应式
  • 两者均是用于结构响应式数据,然而ref次要解决原始值的响应式问题
  • ref返回的响应式数据在JS中应用须要加上.value能力拜访其值,在视图中应用会主动脱ref,不须要.valueref能够接管对象或数组等非原始值,但外部仍然是reactive实现响应式;reactive外部如果接管Ref对象会主动脱ref;应用开展运算符(...)开展reactive返回的响应式对象会使其失去响应性,能够联合toRefs()将值转换为Ref对象之后再开展。
  • reactive外部应用Proxy代理传入对象并拦挡该对象各种操作,从而实现响应式。ref外部封装一个RefImpl类,并设置get value/set value,拦挡用户对值的拜访,从而实现响应式

Vue Ref的作用

  • 获取dom元素this.$refs.box
  • 获取子组件中的datathis.$refs.box.msg
  • 调用子组件中的办法this.$refs.box.open()

router-link和router-view是如何起作用的

剖析

vue-router中两个重要组件router-linkrouter-view,别离起到导航作用和内容渲染作用,然而答复如何失效还真有肯定难度

答复范例

  1. vue-router中两个重要组件router-linkrouter-view,别离起到路由导航作用和组件内容渲染作用
  2. 应用中router-link默认生成一个a标签,设置to属性定义跳转path。实际上也能够通过custom和插槽自定义最终的展示模式。router-view是要显示组件的占位组件,能够嵌套,对应路由配置的嵌套关系,配合name能够显示具名组件,起到更强的布局作用。
  3. router-link组件外部依据custom属性判断如何渲染最终生成节点,外部提供导航办法navigate,用户点击之后理论调用的是该办法,此办法最终会批改响应式的路由变量,而后从新去routes匹配出数组后果,router-view则依据其所处深度deep在匹配数组后果中找到对应的路由并获取组件,最终将其渲染进去。

参考 前端进阶面试题具体解答

Vue中封装的数组办法有哪些,其如何实现页面更新

在Vue中,对响应式解决利用的是Object.defineProperty对数据进行拦挡,而这个办法并不能监听到数组外部变动,数组长度变动,数组的截取变动等,所以须要对这些操作进行hack,让Vue能监听到其中的变动。 那Vue是如何实现让这些数组办法实现元素的实时更新的呢,上面是Vue中对这些办法的封装:

// 缓存数组原型const arrayProto = Array.prototype;// 实现 arrayMethods.__proto__ === Array.prototypeexport const arrayMethods = Object.create(arrayProto);// 须要进行性能拓展的办法const methodsToPatch = [  "push",  "pop",  "shift",  "unshift",  "splice",  "sort",  "reverse"];/** * Intercept mutating methods and emit events */methodsToPatch.forEach(function(method) {  // 缓存原生数组办法  const original = arrayProto[method];  def(arrayMethods, method, function mutator(...args) {    // 执行并缓存原生数组性能    const result = original.apply(this, args);    // 响应式解决    const ob = this.__ob__;    let inserted;    switch (method) {    // push、unshift会新增索引,所以要手动observer      case "push":      case "unshift":        inserted = args;        break;      // splice办法,如果传入了第三个参数,也会有索引退出,也要手动observer。      case "splice":        inserted = args.slice(2);        break;    }    //     if (inserted) ob.observeArray(inserted);// 获取插入的值,并设置响应式监听    // notify change    ob.dep.notify();// 告诉依赖更新    // 返回原生数组办法的执行后果    return result;  });});

简略来说就是,重写了数组中的那些原生办法,首先获取到这个数组的__ob__,也就是它的Observer对象,如果有新的值,就调用observeArray持续对新的值察看变动(也就是通过target__proto__ == arrayMethods来扭转了数组实例的型),而后手动调用notify,告诉渲染watcher,执行update。

Vue为什么没有相似于React中shouldComponentUpdate的生命周期?

考点: Vue的变动侦测原理

前置常识: 依赖收集、虚构DOM、响应式零碎

根本原因是Vue与React的变动侦测形式有所不同

React是pull的形式侦测变动,当React晓得发生变化后,会应用Virtual Dom Diff进行差别检测,然而很多组件实际上是必定不会发生变化的,这个时候须要用shouldComponentUpdate进行手动操作来缩小diff,从而进步程序整体的性能.

Vue是pull+push的形式侦测变动的,在一开始就晓得那个组件产生了变动,因而在push的阶段并不需要手动管制diff,而组件外部采纳的diff形式实际上是能够引入相似于shouldComponentUpdate相干生命周期的,然而通常正当大小的组件不会有适量的diff,手动优化的价值无限,因而目前Vue并没有思考引入shouldComponentUpdate这种手动优化的生命周期.

简述 mixin、extends 的笼罩逻辑

(1)mixin 和 extends mixin 和 extends均是用于合并、拓展组件的,两者均通过 mergeOptions 办法实现合并。

  • mixins 接管一个混入对象的数组,其中混入对象能够像失常的实例对象一样蕴含实例选项,这些选项会被合并到最终的选项中。Mixin 钩子依照传入程序顺次调用,并在调用组件本身的钩子之前被调用。
  • extends 次要是为了便于扩大单文件组件,接管一个对象或构造函数。

    (2)mergeOptions 的执行过程

  • 规范化选项(normalizeProps、normalizelnject、normalizeDirectives)
  • 对未合并的选项,进行判断
if (!child._base) {  if (child.extends) {    parent = mergeOptions(parent, child.extends, vm);  }  if (child.mixins) {    for (let i = 0, l = child.mixins.length; i < l; i++) {      parent = mergeOptions(parent, child.mixins[i], vm);    }  }}
  • 合并解决。依据一个通用 Vue 实例所蕴含的选项进行分类逐个判断合并,如 props、data、 methods、watch、computed、生命周期等,将合并后果存储在新定义的 options 对象里。
  • 返回合并后果 options。

MVVM的优缺点?

长处:

  • 拆散视图(View)和模型(Model),升高代码耦合,提⾼视图或者逻辑的重⽤性: ⽐如视图(View)能够独⽴于Model变动和批改,⼀个ViewModel能够绑定不同的"View"上,当View变动的时候Model不能够不变,当Model变动的时候View也能够不变。你能够把⼀些视图逻辑放在⼀个ViewModel⾥⾯,让很多view重⽤这段视图逻辑
  • 提⾼可测试性: ViewModel的存在能够帮忙开发者更好地编写测试代码
  • ⾃动更新dom: 利⽤双向绑定,数据更新后视图⾃动更新,让开发者从繁琐的⼿动dom中解放

毛病:

  • Bug很难被调试: 因为使⽤双向绑定的模式,当你看到界⾯异样了,有可能是你View的代码有Bug,也可能是Model的代码有问题。数据绑定使得⼀个地位的Bug被疾速传递到别的地位,要定位原始出问题的地⽅就变得不那么容易了。另外,数据绑定的申明是指令式地写在View的模版当中的,这些内容是没方法去打断点debug的
  • ⼀个⼤的模块中model也会很⼤,尽管使⽤⽅便了也很容易保障了数据的⼀致性,过后⻓期持有,不开释内存就造成了破费更多的内存
  • 对于⼤型的图形应⽤程序,视图状态较多,ViewModel的构建和保护的老本都会⽐较⾼。

应用 Object.defineProperty() 来进行数据劫持有什么毛病?

在对一些属性进行操作时,应用这种办法无奈拦挡,比方通过下标形式批改数组数据或者给对象新增属性,这都不能触发组件的从新渲染,因为 Object.defineProperty 不能拦挡到这些操作。更准确的来说,对于数组而言,大部分操作都是拦挡不到的,只是 Vue 外部通过重写函数的形式解决了这个问题。

在 Vue3.0 中曾经不应用这种形式了,而是通过应用 Proxy 对对象进行代理,从而实现数据劫持。应用Proxy 的益处是它能够完满的监听到任何形式的数据扭转,惟一的毛病是兼容性的问题,因为 Proxy 是 ES6 的语法。

Vue 修饰符有哪些

事件修饰符

  • .stop 阻止事件持续流传
  • .prevent 阻止标签默认行为
  • .capture 应用事件捕捉模式,即元素本身触发的事件先在此处解决,而后才交由外部元素进行解决
  • .self 只当在 event.target 是以后元素本身时触发处理函数
  • .once 事件将只会触发一次
  • .passive 通知浏览器你不想阻止事件的默认行为

v-model 的修饰符

  • .lazy 通过这个修饰符,转变为在 change 事件再同步
  • .number 主动将用户的输出值转化为数值类型
  • .trim 主动过滤用户输出的首尾空格

键盘事件的修饰符

  • .enter
  • .tab
  • .delete (捕捉“删除”和“退格”键)
  • .esc
  • .space
  • .up
  • .down
  • .left
  • .right

零碎润饰键

  • .ctrl
  • .alt
  • .shift
  • .meta

鼠标按钮修饰符

  • .left
  • .right
  • .middle

对SSR的了解

SSR也就是服务端渲染,也就是将Vue在客户端把标签渲染成HTML的工作放在服务端实现,而后再把html间接返回给客户端

SSR的劣势:

  • 更好的SEO
  • 首屏加载速度更快

SSR的毛病:

  • 开发条件会受到限制,服务器端渲染只反对beforeCreate和created两个钩子;
  • 当须要一些内部扩大库时须要非凡解决,服务端渲染应用程序也须要处于Node.js的运行环境;
  • 更多的服务端负载。

vue-loader是什么?它有什么作用?

答复范例

  1. vue-loader是用于解决单文件组件(SFCSingle-File Component)的webpack loader
  2. 因为有了vue-loader,咱们就能够在我的项目中编写SFC格局的Vue组件,咱们能够把代码宰割为<template><script><style>,代码会异样清晰。联合其余loader咱们还能够用Pug编写<template>,用SASS编写<style>,用TS编写<script>。咱们的<style>还能够独自作用以后组件
  3. webpack打包时,会以loader的形式调用vue-loader
  4. vue-loader被执行时,它会对SFC中的每个语言块用独自的loader链解决。最初将这些独自的块装配成最终的组件模块

原理

vue-loader会调用@vue/compiler-sfc模块解析SFC源码为一个描述符(Descriptor),而后为每个语言块生成import代码,返回的代码相似上面

// source.vue被vue-loader解决之后返回的代码// import the <template> blockimport render from 'source.vue?vue&type=template'// import the <script> blockimport script from 'source.vue?vue&type=script'export * from 'source.vue?vue&type=script'// import <style> blocksimport 'source.vue?vue&type=style&index=1'script.render = renderexport default script

咱们想要script块中的内容被作为js解决(当然如果是<script lang="ts">被作为ts理),这样咱们想要webpack把配置中跟.js匹配的规定都利用到形如source.vue?vue&type=script的这个申请上。例如咱们对所有*.js配置了babel-loader,这个规定将被克隆并利用到所在Vue SFC

import script from 'source.vue?vue&type=script

将被开展为:

import script from 'babel-loader!vue-loader!source.vue?vue&type=script'

相似的,如果咱们对.sass文件配置了style-loader + css-loader + sass-loader,对上面的代码

<style scoped lang="scss">

vue-loader将会返回给咱们上面后果:

import 'source.vue?vue&type=style&index=1&scoped&lang=scss'

而后webpack会开展如下:

import 'style-loader!css-loader!sass-loader!vue-loader!source.vue?vue&type=style&index=1&scoped&lang=scss'
  • 当解决开展申请时,vue-loader将被再次调用。这次,loader将会关注那些有查问串的申请,且仅针对特定块,它会选中特定块外部的内容并传递给前面匹配的loader
  • 对于<script>块,解决到这就能够了,然而<template><style>还有一些额定工作要做,比方

    • 须要用 Vue 模板编译器编译template,从而失去render函数
    • 须要对 <style scoped>中的CSS做后处理(post-process),该操作在css-loader之后但在style-loader之前

实现上这些附加的loader须要被注入到曾经开展的loader链上,最终的申请会像上面这样:

// <template lang="pug">import 'vue-loader/template-loader!pug-loader!source.vue?vue&type=template'// <style scoped lang="scss">import 'style-loader!vue-loader/style-post-loader!css-loader!sass-loader!vue-loader!source.vue?vue&type=style&index=1&scoped&lang=scss'

Vue 子组件和父组件执行程序

加载渲染过程:

  1. 父组件 beforeCreate
  2. 父组件 created
  3. 父组件 beforeMount
  4. 子组件 beforeCreate
  5. 子组件 created
  6. 子组件 beforeMount
  7. 子组件 mounted
  8. 父组件 mounted

更新过程:

  1. 父组件 beforeUpdate
  2. 子组件 beforeUpdate
  3. 子组件 updated
  4. 父组件 updated

销毁过程:

  1. 父组件 beforeDestroy
  2. 子组件 beforeDestroy
  3. 子组件 destroyed
  4. 父组件 destoryed

Vuex有哪几种属性?

有五种,别离是 State、 Getter、Mutation 、Action、 Module

  • state => 根本数据(数据源寄存地)
  • getters => 从根本数据派生进去的数据
  • mutations => 提交更改数据的办法,同步
  • actions => 像一个装璜器,包裹mutations,使之能够异步。
  • modules => 模块化Vuex

什么是 mixin ?

  • Mixin 使咱们可能为 Vue 组件编写可插拔和可重用的性能。
  • 如果心愿在多个组件之间重用一组组件选项,例如生命周期 hook、 办法等,则能够将其编写为 mixin,并在组件中简略的援用它。
  • 而后将 mixin 的内容合并到组件中。如果你要在 mixin 中定义生命周期 hook,那么它在执行时将优化于组件自已的 hook。

Vue模版编译原理

vue中的模板template无奈被浏览器解析并渲染,因为这不属于浏览器的规范,不是正确的HTML语法,所有须要将template转化成一个JavaScript函数,这样浏览器就能够执行这一个函数并渲染出对应的HTML元素,就能够让视图跑起来了,这一个转化的过程,就成为模板编译。模板编译又分三个阶段,解析parse,优化optimize,生成generate,最终生成可执行函数render。

  • 解析阶段:应用大量的正则表达式对template字符串进行解析,将标签、指令、属性等转化为形象语法树AST。
  • 优化阶段:遍历AST,找到其中的一些动态节点并进行标记,不便在页面重渲染的时候进行diff比拟时,间接跳过这一些动态节点,优化runtime的性能。
  • 生成阶段:将最终的AST转化为render函数字符串。

为什么在 Vue3.0 采纳了 Proxy,摈弃了 Object.defineProperty?

Object.defineProperty 自身有肯定的监控到数组下标变动的能力,然而在 Vue 中,从性能/体验的性价比思考,尤大大就弃用了这个个性。为了解决这个问题,通过 vue 外部解决后能够应用以下几种办法来监听数组
push();pop();shift();unshift();splice();sort();reverse();

因为只针对了以上 7 种办法进行了 hack 解决,所以其余数组的属性也是检测不到的,还是具备肯定的局限性。

Object.defineProperty 只能劫持对象的属性,因而咱们须要对每个对象的每个属性进行遍历。Vue 2.x 里,是通过 递归 + 遍历 data 对象来实现对数据的监控的,如果属性值也是对象那么须要深度遍历,显然如果能劫持一个残缺的对象是才是更好的抉择。

Proxy 能够劫持整个对象,并返回一个新的对象。Proxy 不仅能够代理对象,还能够代理数组。还能够代理动静减少的属性。

二、如何解决

解决跨域的办法有很多,上面列举了三种:

  • JSONP
  • CORS
  • Proxy

而在vue我的项目中,咱们次要针对CORSProxy这两种计划进行开展

CORS

CORS (Cross-Origin Resource Sharing,跨域资源共享)是一个零碎,它由一系列传输的HTTP头组成,这些HTTP头决定浏览器是否阻止前端 JavaScript 代码获取跨域申请的响应

CORS 实现起来十分不便,只须要减少一些 HTTP 头,让服务器能申明容许的拜访起源

只有后端实现了 CORS,就实现了跨域

!

koa框架举例

增加中间件,间接设置Access-Control-Allow-Origin响应头

app.use(async (ctx, next)=> {  ctx.set('Access-Control-Allow-Origin', '*');  ctx.set('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild');  ctx.set('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');  if (ctx.method == 'OPTIONS') {    ctx.body = 200;   } else {    await next();  }})

ps: Access-Control-Allow-Origin 设置为*其实意义不大,能够说是形同虚设,理论利用中,上线前咱们会将Access-Control-Allow-Origin 值设为咱们指标host

Proxy

代理(Proxy)也称网络代理,是一种非凡的网络服务,容许一个(个别为客户端)通过这个服务与另一个网络终端(个别为服务器)进行非间接的连贯。一些网关、路由器等网络设备具备网络代理性能。个别认为代理服务有利于保障网络终端的隐衷或平安,避免攻打

计划一

如果是通过vue-cli脚手架工具搭建我的项目,咱们能够通过webpack为咱们起一个本地服务器作为申请的代理对象

通过该服务器转发申请至指标服务器,失去后果再转发给前端,然而最终公布上线时如果web利用和接口服务器不在一起仍会跨域

vue.config.js文件,新增以下代码

amodule.exports = {    devServer: {        host: '127.0.0.1',        port: 8084,        open: true,// vue我的项目启动时主动关上浏览器        proxy: {            '/api': { // '/api'是代理标识,用于通知node,url后面是/api的就是应用代理的                target: "http://xxx.xxx.xx.xx:8080", //指标地址,个别是指后盾服务器地址                changeOrigin: true, //是否跨域                pathRewrite: { // pathRewrite 的作用是把理论Request Url中的'/api'用""代替                    '^/api': ""                 }            }        }    }}

通过axios发送申请中,配置申请的根门路

axios.defaults.baseURL = '/api'

计划二

此外,还可通过服务端实现代理申请转发

express框架为例

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

计划三

通过配置nginx实现代理

server {    listen    80;       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;    }}