共计 11558 个字符,预计需要花费 29 分钟才能阅读完成。
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) // 0
count.value++
console.log(count.value) // 1
const 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.prototype
export 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> block
import render from 'source.vue?vue&type=template'
// import the <script> block
import script from 'source.vue?vue&type=script'
export * from 'source.vue?vue&type=script'
// import <style> blocks
import 'source.vue?vue&type=style&index=1'
script.render = render
export 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;
}
}