1、你知道vue的模板语法用的是哪个web模板引擎的吗?说说你对这模板引擎的理解。
使用的Mustache模版。
模板引擎:
负责组装数据,以另外一种形式或外观展现数据。
优点:
可维护性(后期改起来方便);
可扩展性(想要增加功能,增加需求方便);
开发效率提高(程序逻辑组织更好,调试方便);
看起来舒服(不容易写错)
2、你知道v-model的原理吗?
v-model只是一个语法糖,其内部实现原理就是使用v-bind
和input
事件监听值的改变。
changeValue(e){
value = e.target.value
}
3、使用过vue开发过多语言项目吗?说说你的做法
使用vue-i18n。具体使用请参考vue-i18n。
安装:npm install vue-i18n –save-dev
创建语言文件:language-zh.js和language-en.js 分别对应中文和英文。
在main.js中引入。
切换语言。
language-zh.js:
export const lang = {
home:'首页‘,
name:'姓名'
}
language-en.js:
export const lang = {
home:'home‘,
name:'name'
}
main.js:
import VueI18n from 'vue-i18n'
Vue.use(VueI18n) // 通过插件的形式挂载,通过全局方法 Vue.use() 使用插件const i18n = new VueI18n({
locale: 'zh', // 语言标识 //this.$i18n.locale // 通过切换locale的值来实现语言切换
messages: {
'zh': require('./VueI18n/language-zh'), //
'en': require('./VueI18n/language-en')
}
})
Vue.config.productionTip = false
new Vue({
el: '#app',
router,
i18n,
components: { App },
template: ''
})
切换语言:
changeLaguages () {
console.log(this.$i18n.locale)
let lang = this.$i18n.locale === 'zh' ? 'en' : 'zh'
this.$i18n.locale = lang
}
4、在使用计算属性时,函数名可以和data数据源中的属性同名吗?为什么?
不可以。因为无论是计算属性、data、还是props,最终都会挂载到vm实例上,因此三者不可以同名。
在src/core/instance/state.js#L202
这里,会做一个检查。
而且在初始化vm的时候,会依次初始化props、mehtods、data,computed等。这是初始化的源码:
export function initState (vm: Component) {
vm._watchers = []
const opts = vm.$options
if (opts.props) initProps(vm, opts.props)
if (opts.methods) initMethods(vm, opts.methods)
if (opts.data) {
initData(vm)
} else {
observe(vm._data = {}, true /* asRootData */)
}
if (opts.computed) initComputed(vm, opts.computed)
if (opts.watch && opts.watch !== nativeWatch) {
initWatch(vm, opts.watch)
}
}
5、vue中data数据源中的属性可以和methods中的方法名重名吗?为什么?
不可以。
在初始化data的时候,程序会进行检查,src/core/instance/state.js#L113
,在initState
函数:
function initData (vm: Component) {
let data = vm.$options.data
data = vm._data = typeof data === 'function'
? getData(data, vm)
: data || {}
if (!isPlainObject(data)) {
data = {}
process.env.NODE_ENV !== 'production' && warn(
'data functions should return an object:\n' +
'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function',
vm
)
}
// proxy data on instance
const keys = Object.keys(data)
const props = vm.$options.props
const methods = vm.$options.methods
let i = keys.length
while (i--) {
const key = keys[i]
if (process.env.NODE_ENV !== 'production') {
if (methods && hasOwn(methods, key)) {//这里会对同名属性和方法进行警告
warn(
`Method "${key}" has already been defined as a data property.`,
vm
)
}
}
if (props && hasOwn(props, key)) {//这里会对同名的props和属性进行警告
process.env.NODE_ENV !== 'production' && warn(
`The data property "${key}" is already declared as a prop. ` +
`Use prop default value instead.`,
vm
)
} else if (!isReserved(key)) {
proxy(vm, `_data`, key)
}
}
// observe data
observe(data, true /* asRootData */)
}
5、怎么给vue定义全局方法?
挂载到Vue的原型上,即:Vue.prototype.methodsName = function(){}。
使用mixin,即Vue.use(mixins)。
6、vue2.0中不再支持在v-html中使用过滤器了怎么办?
使用全局方法。推荐
使用filters过滤器。
使用computed。
使用methods。
全局方法:
Vue.prototype.msg = function(msg){
return msg.replace("\n","
")
};
<div v-html="msg(content)">div>
filters过滤器:
filters:{
msg:function(msg){
return msg.replace(/\n/g,"
")
}
}
computed:
computed:{
msg:function(msg){
return msg.replace(/\n/g,"
")
}
}
mehtods:
methods:{
msg:function(msg){
return msg.replace(/\n/g,"
")
}
}
7、使用vue后怎么针对搜索引擎做SEO优化?
SSR服务端渲染
NUXT同构
prerender-spa-plugin 预渲染
8、和keep-alive相关的生命周期有哪些?描述下这些生命周期
activated 和 deactivated。
activated:当页面进入的时候,依次触发:created –> mounted –> activated。
deactivated:当退出页面的时候触发deactivated,当在此前进或者后退的时候之后触发activated。
9、你知道vue中key的原理吗?
src\core\vdom\patch.js - updateChildren()
1、key的作用主要是为了高效的更新虚拟DOM,其原理是vue在patch过程中通过key可以精准判断两个节点是否是同一个,从而避免频繁更新不同元素,使得整个patch过程更加高效,减少DOM操作量,提高性能。
2、同时,在vue中使用相同标签名的元素过渡的时候需要使用key进行区分,否则vue之后更新其内部属性而不会触发过渡效果。
10、 vue如何重置data?
this.$data: 获取当前状态下的data。
this.$options.data():获取当前组件初始化状态下的data。
so:Object.assgin( this.$data,this.$options.data() )
11、谈谈你对mvc 、mvp和mvvm的理解?
在web1.0时代,并没有web前端的概念,当时开发一个web应用基本是由asp、.net、java等后台人员完成。项目通常包含html、css、js等文件。这样做的优势就是开发简单快捷。缺点就是jsp文件难以维护。
mvc:为了解决上述问题,使前后端职责更加清晰、代码更容易维护、于是mvc开发模式和框架应运而生。在这个模式下,前端主要负责v,即视图层view,前端使用html模版渲染html。后台负责c层和m层,即控制层和数据层。当用户发起请求时,后端根据用户请求路径,返回相应的页面。这样的缺点就是前端页面开发效率不高、前后端职责还不够清晰明确。
在web2.0时代,随着ajax的出现,让前端可以使用ajax和后台进行交互,前后端职责更加清晰。于是浏览器和服务器之间的整体架构变成了这样:
页面通过ajax与服务器进行交互,前端人员只负责页面部分,数据部分由后台获取。而且使用ajax可以局部刷新页面,大大降低了服务端压力和流量消耗,而且对用户端体验也更加友好。
mvp:随着业务的增加,我们对项目的不断跌倒,就会导致view层越来越庞大,controller层显得越发单薄。于是人们就这v层和m层中间添加了p层。由p层负责m层和v层之间的数据流动,防止二者直接进行数据流动。
我们可以通过看到,presenter负责和Model进行双向交互,还和View进行双向交互。这种交互方式,相对于MVC来说少了一些灵活,VIew变成了被动视图,并且本身变得很小。虽然它分离了View和Model。但是应用逐渐变大之后,导致presenter的体积增大,难以维护。
mvvm:首先,何为MVVM呢?MVVM可以分解成(Model-View-VIewModel)。ViewModel可以理解为在presenter基础上的进阶版。如图所示:
ViewModel通过实现一套数据响应式机制自动响应Model中数据变化;
同时Viewmodel会实现一套更新策略自动将数据变化转换为视图更新;
通过事件监听响应View中用户交互修改Model中数据。
这样在ViewModel中就减少了大量DOM操作代码。
MVVM在保持View和Model松耦合的同时,还减少了维护它们关系的代码,使用户专注于业务逻辑,兼顾开发效率和可维护性。
总结:
这三者都是框架模式,它们设计的目标都是为了解决Model和View的耦合问题。
MVC模式出现较早主要应用在后端,如Spring MVC、ASP.NET MVC等,在前端领域的早期也有应用,如Backbone.js。它的优点是分层清晰,缺点是数据流混乱,灵活性带来的维护性问题。
MVP模式在是MVC的进化形式,Presenter作为中间层负责MV通信,解决了两者耦合问题,但P层过于臃肿会导致维护问题。
MVVM模式在前端领域有广泛应用,它不仅解决MV耦合问题,还同时解决了维护两者映射关系的大量繁杂代码和DOM操作代码,在提高开发效率、可读性同时还保持了优越的性能表现。
12、你知道style加scope属性的原理和作用吗
用途:防止全局同名CSS污染
原理:在标签加上v-data-something属性,再在选择器时加上对应[v-data-something],即CSS带属性选择器,以此完成类似作用域的选择方式。
13、vue边界情况有哪些
访问根实例:this.$root
访问父组件:this.$parent
访问子组件:this.$refs
依赖注入:provide inject
组件递归引用
14、子组件如何访问父组件
直接在子组件中使用this.$parent.methods
子组件使用$emit发送一个事件,在父组件中设置监听
父组件将事件通过属性传递给子组件
15、watch/methods中使用箭头函数会怎么样?
不能使用箭头函数。因为箭头函数的this默认绑定父级作用域,而不是vue实例。如果使用的是npm安装的vue,则此时this指向当前组件:
如果使用的是script标签,则this指向window。
16 、vue如何配置favicon
在插件配置中的HtmlWebpackPlugin中配置
new HtmlWebpackPlugin({
template: 'src/index.html',
favicon:''
})
在html模版中配置:
<link rel="icon" href="/assets/title.png" type="image/x-icon" />
17、vue的错误处理你知道多少?
errorHandler
具体请看:errorHandler
Vue.config.errorHandler = function (err, vm, info) {
// handle error
// `info` is a Vue-specific error info, e.g. which lifecycle hook
// the error was found in. Only available in 2.2.0+
}
其中:err
指的是error
对象本身,info指的是错误信息,vm指的是vue实例。
warnHandler
Vue.config.warnHandler = function (msg, vm, trace) {
// `trace` is the component hierarchy trace
console.log(`Warn: ${msg}\nTrace: ${trace}`);
}
trace代表组件树。
例如:我们引入一个不存在的变量
<div id="app" v-cloak>
Hello, {{name}}
</div>
就会提示:
Warn: Property or method 'name' is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property. See: https://vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.
Trace:
(found in <Root>)
renderError:和前面两个不同,这个技巧不适用于全局,和组件相关。并且只适用于非生产环境。
const app = new Vue({
el:'#app',
renderError (h, err) {
return h('pre', { style: { color: 'red' }}, err.stack)
}
})
举个栗子:
<div id="app" v-cloak>
Hello, {{name2}}
</div>
<script>
const app = new Vue({
el:'#app',
computed:{
name2() {
return x;
}
}
})
</script>
ReferenceError: x is not defined
at Vue.name2 (pen.js:45:7)
at Watcher.get (https://unpkg.com/vue@2.6.11/dist/vue.js:4478:27)
at Watcher.evaluate (https://unpkg.com/vue@2.6.11/dist/vue.js:4583:23)
at Proxy.computedGetter (https://unpkg.com/vue@2.6.11/dist/vue.js:4832:19)
at Proxy.eval (eval at createFunction (https://unpkg.com/vue@2.6.11/dist/vue.js:11649:14), <anonymous>:3:68)
at Vue._render (https://unpkg.com/vue@2.6.11/dist/vue.js:3551:24)
at Vue.updateComponent (https://unpkg.com/vue@2.6.11/dist/vue.js:4067:23)
at Watcher.get (https://unpkg.com/vue@2.6.11/dist/vue.js:4478:27)
at new Watcher (https://unpkg.com/vue@2.6.11/dist/vue.js:4467:14)
at mountComponent (https://unpkg.com/vue@2.6.11/dist/vue.js:4074:5)
errorCaptured
类型:(err: Error, vm: Component, info: string) => ?boolean
当捕获一个来自子孙组件的错误时被调用。此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回 false
以阻止该错误继续向上传播。
18、vue文件中style、script是必须的吗
style不是必须的。但是script是必须的:export default {}
19、如果vue变量名出现以_或者$开头的时候,会出现什么情况?
会报undefined
。因为在initState
的时候,vm
会代理所有的data
,如果出现有_
或者$
开头的变量名,可能会和vm
内部属性冲突,vue
不会代理该属性。可以通过vm.$data.
访问。
20、vue使用v-for遍历对象的时候,是按什么顺序遍历的?如何保证遍历顺序?
首先,会判断对象是否有iterator
接口,如果有,则循环执行next()
方法。
没有iterator
接口的情况下,使用Object.keys()
遍历,但是在不同浏览器的情况下,遍历的结果不可能完全一致。
可以将对象放在数组中,遍历数组可以保证遍历顺序。
21、vue想扩展组件时该怎么做?
Vue.mixin混入
Vue.extend
solt
22、vue中$attrs 和 $listeners的使用场景
组件传值的时候会用到 爷爷在父亲组件传递值,父亲组件会通过$attrs获取到不在父亲props里面的所有属性,父亲组件通过在孙子组件上绑定$attrs 和 $listeners 使孙组件获取爷爷传递的值并且可以调用在爷爷那里定义的方法
A组件:爷爷级组件
<template>
<div>
<B :a="a" :b="b" :c="c" v-bind="$attrs" @test="test" @test2="test2" v-on="$listeners"/>
</div>
</template>
<script>
import B from './B.vue'
export default {
name:"A",
components:{
B
},
data(){
return {
a:1,
b:2,
c:3
}
},
methods:{
test(){
alert(0)
},
test2(){
alert(2)
}
}
}
</script>
B组件:父亲级组件
<template>
<div>
<p>attrs: {{ $attrs }}</p>
<p style="background:#ccc;" @click="$listeners.test">props: {{ a }} </p>
<C v-bind="$attrs"/>
</div>
</template>
<script>
import C from './C.vue'
export default {
components:{
C
},
name:"B",
props:['a'],
data(){
return {
}
}
}
</script>
C:孙子级组件
<template>
<div>
<p>C : {{$attrs }}</p>
</div>
</template>
<script>
export default {
name:"C",
props:['name'],
inheritAttrs:false,
data(){
return {
}
}
}
</script>
23、vue为什么要求组件模版只能有一个根结点
因为vue组件最终是要被各种loader打包解析的,而我们必须为loader指定一个入口。而且,模版最终是会被编译成vdom的,所以必须有一个根结点来递归遍历其子节点,渲染成一个“树”。