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
,不须要.value
;ref
能够接管对象或数组等非原始值,但外部仍然是reactive
实现响应式;reactive
外部如果接管Re
f对象会主动脱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-link
和router-view
,别离起到导航作用和内容渲染作用,然而答复如何失效还真有肯定难度
答复范例
vue-router
中两个重要组件router-link
和router-view
,别离起到路由导航作用和组件内容渲染作用- 应用中
router-link
默认生成一个a
标签,设置to
属性定义跳转path
。实际上也能够通过custom
和插槽自定义最终的展示模式。router-view
是要显示组件的占位组件,能够嵌套,对应路由配置的嵌套关系,配合name
能够显示具名组件,起到更强的布局作用。 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是什么?它有什么作用?
答复范例
vue-loader
是用于解决单文件组件(SFC
,Single-File Component
)的webpack loader
- 因为有了
vue-loader
,咱们就能够在我的项目中编写SFC
格局的Vue
组件,咱们能够把代码宰割为<template>
、<script>
和<style>
,代码会异样清晰。联合其余loader
咱们还能够用Pug
编写<template>
,用SASS
编写<style>
,用TS
编写<script>
。咱们的<style>
还能够独自作用以后组件 webpack
打包时,会以loader
的形式调用vue-loader
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 子组件和父组件执行程序
加载渲染过程:
- 父组件 beforeCreate
- 父组件 created
- 父组件 beforeMount
- 子组件 beforeCreate
- 子组件 created
- 子组件 beforeMount
- 子组件 mounted
- 父组件 mounted
更新过程:
- 父组件 beforeUpdate
- 子组件 beforeUpdate
- 子组件 updated
- 父组件 updated
销毁过程:
- 父组件 beforeDestroy
- 子组件 beforeDestroy
- 子组件 destroyed
- 父组件 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
我的项目中,咱们次要针对CORS
或Proxy
这两种计划进行开展
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; }}