vue综合指南(二)

​🌈个人主页:前端青山
🔥系列专栏:Vue篇
🔖人终将被年少不可得之物困其一生

依旧青山,本期给大家带来Vuet篇专栏内容:vue综合指南(二)

目录

21、介绍虚拟DOM

22、vue生命周期的理解

23、vue父组件向子组件传递数据?

24、vue-router 路由实现原理

25、v-if 和 v-show 区别

26、$route和$router的区别

27、对比 jQuery ,Vue 有什么不同

28、Vue 中怎么自定义指令

29、Vue 中怎么自定义过滤器

30、Vue 中 key 的作用

31、Vue 的核心思想是什么

32、Vue 等单页面应用的优缺点

33、Vue-router 使用params与query传参有什么区别

34、子组件像父组件传递事件?

35、Vue中 的作用

36、如何让CSS只在当前组件中起作用?

37、如何获取dom?

38、说出几种vue当中的指令和它的用法?

39、Vue Router的钩子函数有哪些?他们有哪些参数?

40、说明一下封装vue组件的原则和方法


21、介绍虚拟DOM

先介绍浏览器加载一个HTML文件需要做哪些事,帮助我们理解为什么我们需要虚拟DOM。webkit引擎的处理流程,一图胜千言:

img

所有浏览器的引擎工作流程都差不多,如上图大致分5步:创建DOM tree –> 创建Style Rules -> 构建Render tree -> 布局Layout –> 绘制Painting

第一步,用HTML分析器,分析HTML元素,构建一颗DOM树。

第二步:用CSS分析器,分析CSS文件和元素上的inline样式,生成页面的样式表。

第三步:将上面的DOM树和样式表,关联起来,构建一颗Render树。这一过程又称为Attachment。每个DOM节点都有attach方法,接受样式信息,返回一个render对象(又名renderer)。这些render对象最终会被构建成一颗Render树。

第四步:有了Render树后,浏览器开始布局,会为每个Render树上的节点确定一个在显示屏上出现的精确坐标值。

第五步:Render数有了,节点显示的位置坐标也有了,最后就是调用每个节点的paint方法,让它们显示出来。

当你用传统的源生api或jQuery去操作DOM时,浏览器会从构建DOM树开始从头到尾执行一遍流程。比如当你在一次操作时,需要更新10个DOM节点,理想状态是一次性构建完DOM树,再执行后续操作。但浏览器没这么智能,收到第一个更新DOM请求后,并不知道后续还有9次更新操作,因此会马上执行流程,最终执行10次流程。显然例如计算DOM节点的坐标值等都是白白浪费性能,可能这次计算完,紧接着的下一个DOM更新请求,这个节点的坐标值就变了,前面的一次计算是无用功。

即使计算机硬件一直在更新迭代,操作DOM的代价仍旧是昂贵的,频繁操作还是会出现页面卡顿,影响用户的体验。真实的DOM节点,哪怕一个最简单的div也包含着很多属性,可以打印出来直观感受一下:

img

虚拟DOM就是为了解决这个浏览器性能问题而被设计出来的。例如前面的例子,假如一次操作中有10次更新DOM的动作,虚拟DOM不会立即操作DOM,而是将这10次更新的diff内容保存到本地的一个js对象中,最终将这个js对象一次性attach到DOM树上,通知浏览器去执行绘制工作,这样可以避免大量的无谓的计算量。

实现虚拟DOM

我们来实现一个虚拟DOM。例如一个真实的DOM节点:代码见仓库里的src/firstStep

<div id="real-container"><p>Real DOM</p><div>cannot update</div><ul><li className="item">Item 1</li><li className="item">Item 2</li><li className="item">Item 3</li></ul>
</div>

用js对象来模拟DOM节点如下:

const tree = Element('div', { id: 'virtual-container' }, [Element('p', {}, ['Virtual DOM']),Element('div', {}, ['before update']),Element('ul', {}, [Element('li', { class: 'item' }, ['Item 1']),Element('li', { class: 'item' }, ['Item 2']),Element('li', { class: 'item' }, ['Item 3']),]),
]);
​
const root = tree.render();
document.getElementById('virtualDom').appendChild(root);

用js对象模拟DOM节点的好处是,页面的更新可以先全部反映在js对象上,操作内存中的js对象的速度显然要快多了。等更新完后,再将最终的js对象映射成真实的DOM,交由浏览器去绘制。

那具体怎么实现呢?看一下Element方法的具体实现:

function Element(tagName, props, children) {if (!(this instanceof Element)) {return new Element(tagName, props, children);}
​this.tagName = tagName;this.props = props || {};this.children = children || [];this.key = props ? props.key : undefined;
​let count = 0;this.children.forEach((child) => {if (child instanceof Element) {count += child.count;}count++;});this.count = count;
}

第一个参数是节点名(如div),第二个参数是节点的属性(如class),第三个参数是子节点(如ul的li)。除了这三个参数会被保存在对象上外,还保存了key和count。

img

有了js对象后,最终还需要将其映射成真实的DOM:

Element.prototype.render = function() {const el = document.createElement(this.tagName);const props = this.props;
​for (const propName in props) {setAttr(el, propName, props[propName]);}
​this.children.forEach((child) => {const childEl = (child instanceof Element) ? child.render() : document.createTextNode(child);el.appendChild(childEl);});
​return el;
};

上面都是自解释代码,根据DOM名调用源生的createElement创建真实DOM,将DOM的属性全都加到这个DOM元素上,如果有子元素继续递归调用创建子元素,并appendChild挂到该DOM元素上。这样就完成了从创建虚拟DOM到将其映射成真实DOM的全部工作。

22、vue生命周期的理解

当面试官问:“谈谈你对vue的生命周期的理解”,听到这句话你是不是心里暗自窃喜:这也太容易了吧,不就是beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestroy、destroyed 这几个钩子函数么,创建=>挂载=>更新=>销毁,So easy !!!

非也非也。如果你只是简单罗列出这几个钩子函数的名称,不具体深入阐述的话,你这样的回答很难令面试官满意。如何才能以点带面深入阐述自己对vue的生命周期理解,从而让面试官对你留下好印象呢?

别急,闰土大叔来告诉你,下次再碰到这个问题,你可以直接甩给面试官下面这张Image就OK了~

clipboard.png

当然,甩张Image给面试官这句话肯定是开玩笑的(适度幽默,缓解紧张气氛)。不过这张流程图还是有用的,因为它是我从Vue官网上拷贝下来的,只要你能理解了这张图,也就对Vue的生命周期有了一个大致的了解。那么接下来,闰土大叔将手摸手教你如何深入浅出地说出令面试官满意的、有亮点的回答。

在谈到Vue的生命周期的时候,我们首先需要创建一个实例,也就是在 new Vue ( ) 的对象过程当中,首先执行了init(init是vue组件里面默认去执行的),在init的过程当中首先调用了beforeCreate,然后在injections(注射)和reactivity(反应性)的时候,它会再去调用created。所以在init的时候,事件已经调用了,我们在beforeCreate的时候千万不要去修改data里面赋值的数据,最早也要放在created里面去做(添加一些行为)。

当created完成之后,它会去判断instance(实例)里面是否含有“el”option(选项),如果没有的话,它会调用vm.$mount(el)这个方法,然后执行下一步;如果有的话,直接执行下一步。紧接着会判断是否含有“template”这个选项,如果有的话,它会把template解析成一个render function ,这是一个template编译的过程,结果是解析成了render函数:

render (h) {return h('div', {}, this.text)
}

解释一下,render函数里面的传参h就是Vue里面的createElement方法,return返回一个createElement方法,其中要传3个参数,第一个参数就是创建的div标签;第二个参数传了一个对象,对象里面可以是我们组件上面的props,或者是事件之类的东西;第三个参数就是div标签里面的内容,这里我们指向了data里面的text。

使用render函数的结果和我们之前使用template解析出来的结果是一样的。render函数是发生在beforeMount和mounted之间的,这也从侧面说明了,在beforeMount的时候,$el还只是我们在HTML里面写的节点,然后到mounted的时候,它就把渲染出来的内容挂载到了DOM节点上。这中间的过程其实是执行了render function的内容。

在使用.vue文件开发的过程当中,我们在里面写了template模板,在经过了vue-loader的处理之后,就变成了render function,最终放到了vue-loader解析过的文件里面。这样做有什么好处呢?原因是由于在解析template变成render function的过程,是一个非常耗时的过程,vue-loader帮我们处理了这些内容之后,当我们在页面上执行vue代码的时候,效率会变得更高。

beforeMount在有了render function的时候才会执行,当执行完render function之后,就会调用mounted这个钩子,在mounted挂载完毕之后,这个实例就算是走完流程了。

后续的钩子函数执行的过程都是需要外部的触发才会执行。比如说有数据的变化,会调用beforeUpdate,然后经过Virtual DOM,最后updated更新完毕。当组件被销毁的时候,它会调用beforeDestory,以及destoryed。

这就是vue实例从新建到销毁的一个完整流程,以及在这个过程中它会触发哪些生命周期的钩子函数。那说到这儿,可能很多童鞋会问,钩子函数是什么意思?

钩子函数,其实和回调是一个概念,当系统执行到某处时,检查是否有hook,有则回调。说的更直白一点,每个组件都有属性,方法和事件。所有的生命周期都归于事件,在某个时刻自动执行。

其实,当你跟面试官阐述到这儿的时候,面试官基本上已经满意你的回答了,隐约看到了你的技术功底。当然,如果你还想更进一步,让面试官对你刮目相看,达到加分的效果,你还可以这样说:

在这个过程当中,Vue为我们提供了renderError方法,这个方法只有在开发的时候它才会被调用,在正式打包上线的过程当中,它是不会被调用的。它主要是帮助我们调试render里面的一些错误。

renderError (h, err) {return h('div', {}, err.stack)
}

有且只有当render方法里面报错了,才会执行renderError方法。

所以我们主动让render函数报个错:

render (h) {throw new TypeError('render error')
}

clipboard.png

如图所示,渲染出来的就是Error信息了。还有一点,renderError只有在本组件的render方法报错的情况下它才会被调用。

23、vue父组件向子组件传递数据?

通过props

24、vue-router 路由实现原理

更新视图但不重新请求页面,是前端路由原理的核心之一,目前在浏览器环境中这一功能的实现主要有2种方式:

    1.Hash --- 利用 URL 中的hash("#");

    2.利用 History interface 在HTML5中新增的方法。

  Vue 中,它是通过 mode 这一参数控制路由的实现模式:

const router=new VueRouter({mode:'history',routes:[...]
})

  创建 VueRouter 的实例对象时,mode 以构造参数的形式传入,如下代码:

源码

mode 参数:

    1.默认 hash

  1. history。如果浏览器不支持 history 新特性,则采用 hash

    1. 如果不在浏览器环境下,就采用 abstract(Node环境下)

    

img

mode 区别:

  1. mode:"hash" 多了 “#”

http://localhost:8080/#/login

    2.mode:"history"

http://localhost:8080/recommend

HashHistory:

    hash("#") 的作用是加载 URL 中指示网页中的位置。

本身以及它后面的字符称职位 hash,可通过 window.location.hash 获取

特点:

  1. hash 虽然出现在 url 中,但不会被包括在 http 请求中,它是用来指导浏览器动作的,对服务器端完全无用,因此,改变 hash 不会重新加载页面。

    可以为 hash 的改变添加监听事件:

      window.addEventListener("hashchange",funcRef,false)
  1. 每一次改变 hash(window.localtion.hash),都会在浏览器访问历史中增加一个记录。

    利用 hash 的以上特点,就可以来实现前端路由"更新视图但不重新请求页面"的功能了。

    HashHistory 拥有两个方法,一个是 push, 一个是 replace

两个方法:HashHistory.push() 和 HashHistory.replace()

  

    HashHistory.push() 将新路由添加到浏览器访问历史的栈顶

img

HashHisttory.push()

    

img

    从设置路由改变到视图更新的流程:  

$router.push() --> HashHistory.push() --> History.transitionTo() --> History.updateRoute() --> {app._route = route} --> vm.render()

  

    解析:

复制代码

1 $router.push() //调用方法
​
2 HashHistory.push() //根据hash模式调用,设置hash并添加到浏览器历史记录(添加到栈顶)(window.location.hash= XXX)
​
3 History.transitionTo() //监测更新,更新则调用History.updateRoute()
​
4 History.updateRoute() //更新路由
​
5 {app._route= route} //替换当前app路由
​
6 vm.render() //更新视图

复制代码

    transitionTo() 方法是父类中定义的是用来处理路由变化中的基础逻辑的,push() 方法最主要的是对 window 的 hash 进行了直接赋值:

  

window.location.hash=route.fullPath

    hash 的改变会自动添加到浏览器的访问历史记录中。     那么视图的更新是怎么实现的呢,我们来看看父类 History 中的 transitionTo() 方法:

    

复制代码

transitionTo (location: RawLocation, onComplete?: Function, onAbort?: Function) {const route = this.router.match(location, this.current)this.confirmTransition(route, () => {this.updateRoute(route)...})
}
​
updateRoute (route: Route) {this.cb && this.cb(route)}
​
listen (cb: Function) {this.cb = cb
}

复制代码

    可以看到,当路由变化时,调用了Hitory中的this.cb方法,而this.cb方法是通过History.listen(cb)进行设置的,回到VueRouter类定义中,找到了在init()中对其进行了设置:

    

复制代码

init (app: any /* Vue component instance */) {this.apps.push(app)
​history.listen(route => {this.apps.forEach((app) => {app._route = route})})
}

复制代码

    HashHistory.replace()

      replace()方法与push()方法不同之处在于,它并不是将新路由添加到浏览器访问历史的栈顶,而是替换掉当前的路由

HashHisttory.replace()

      

img

HTML5History

    History interface 是浏览器历史记录栈提供的接口,通过back()、forward()、go()等方法,我们可以读取浏览器历史记录栈的信息,进行各种跳转操作。``

    从 HTML5开始,History interface 提供了2个新的方法:pushState()、replaceState() 使得我们可以对浏览器历史记录栈进行修改:

  window.history.pushState(stateObject,title,url)window.history,replaceState(stateObject,title,url)

    

    stateObject:当浏览器跳转到新的状态时,将触发 Popstate 事件,该事件将携带这个 stateObject 参数的副本

    title:所添加记录的标题

    url:所添加记录的 url``

    

    这2个方法有个共同的特点:当调用他们修改浏览器历史栈后,虽然当前url改变了,但浏览器不会立即发送请求该url,这就为单页应用前端路由,更新视图但不重新请求页面提供了基础

    

    1.push

      与hash模式类似,只是将window.hash改为history.pushState

    2.replace

      与hash模式类似,只是将window.replace改为history.replaceState

    3.监听地址变化

      在HTML5History的构造函数中监听popState(window.onpopstate)

25、v-if 和 v-show 区别

1.共同点

v-if 和 v-show 都可以动态地显示DOM元素

2.区别

(1)手段:

v-if 是动态的向DOM树内添加或者删除DOM元素; v-show 是通过设置DOM元素的display样式属性控制显隐;

(2)编译过程:

v-if 切换有一个局部编译/卸载的过程,切换过程中合适地销毁和重建内部的事件监听和子组件; v-show只是简单的基于css切换;

(3)编译条件:

v-if 是惰性的,如果初始条件为假,则什么也不做;只有在条件第一次变为真时才开始局部编译(编译被缓存?编译被缓存后,然后再切换的时候进行局部卸载); v-show 是在任何条件下(首次条件是否为真)都被编译,然后被缓存,而且DOM元素保留;

(4)性能消耗:

v-if 有更高的切换消耗; v-show 有更高的初始渲染消耗;

(5)使用场景:

v-if 适合运营条件不大可能改变; v-show 适合频繁切换。

26、$route$router的区别

router.push方法,路由跳转,传递参数等。

返回上一个历史history用$router.to(-1)

$route为当前router跳转对象。里面可以获取当前路由的name,path,query,parmas等 可以拿里边的值来做各种判断。

27、对比 jQuery ,Vue 有什么不同

1.jquery介绍:想必大家都用过jquery吧,这个曾经也是现在依然最流行的web前端js库,可是现在无论是国内还是国外他的使用率正在渐渐被其他的js库所代替,随着浏览器厂商对HTML5规范统一遵循以及ECMA6在浏览器端的实现,jquery的使用率将会越来越低

2.vue介绍:vue是一个兴起的前端js库,是一个精简的MVVM。从技术角度讲,Vue.js 专注于 MVVM 模型的 ViewModel 层。它通过双向数据绑定把 View 层和 Model 层连接了起来,通过对数据的操作就可以完成对页面视图的渲染。当然还有很多其他的mvmm框架如Angular,React都是大同小异,本质上都是基于MVVM的理念。 然而vue以他独特的优势简单,快速,组合,紧凑,强大而迅速崛起

3.vue和jquey对比

jQuery是使用选择器($)选取DOM对象,对其进行赋值、取值、事件绑定等操作,其实和原生的HTML的区别只在于可以更方便的选取和操作DOM对象,而数据和界面是在一起的。比如需要获取label标签的内容:$("lable").val();,它还是依赖DOM元素的值。

Vue则是通过Vue对象将数据和View完全分离开来了。对数据进行操作不再需要引用相应的DOM对象,可以说数据和View是分离的,他们通过Vue对象这个vm实现相互的绑定。这就是传说中的MVVM。

28、Vue 中怎么自定义指令

指令的概念

指令是指可以控制操作 DOM 的一些小命令,通常以 v- 前缀出现的特殊特性。

例如我们经常使用的v-ifv-showv-bindv-onv-html等。

使用指令时,你可以传递值,字符串,也可以给指令添加参数,修饰符等等。比如:

1.传递值

<p v-if="isShow">你好,我是六哥</p>
let vm = new Vue({el: "#app",data: {isShow: true}
});

2.字符串

<p v-text="'hello world'"></p>

3.添加参数

// class,style 就是传给指令的参数
​
<div v-bind:class="classObj"></div>
<div v-bind:style="classObj"></div>

4.修饰符

// prevent 指令的修饰符
<button v-on:submit.prevent="onSubmit"></button>

以上是你经常使用指令的方式,了解这些之后,它可以帮助我们更好的认识自定义指令的 一些参数问题。下面就来看看自定义指令。

自定义指令

指令的注册方式和「过滤器」、「混入」、「组件」注册的方式一样都分为两种:一是全局注册,二是局部注册。

1.全局注册

Vue.directive('name', {})

2.局部注册

directives: {name: {}
}

然后在模版中直接使用即可。

<p v-name>你好,六哥在这</p>

我个人更倾向于使用全局注册的方式,因为既然已经使用了自定义指令,应该是通用,可复用的。

所以提供整个项目使用的指令才更有价值,而不仅仅只限于某个组件内部。如果单一地方使用直接把功能搂出就行了,何必费这力气。

继续来看具体的实现方式。

Vue 提供了自定义指令的几个钩子函数:

  • bind:指令第一次绑定到元素时调用,只执行一次。

  • inserted:被绑定的元素,插入到父节点的 DOM 中时调用。

  • update:组件更新时调用。

  • componentUpdated:组件与子组件更新时调用。

  • unbind:指令与元素解绑时调用,只执行一次。

除update 与 componentUpdated 钩子函数之外,每个钩子函数都含有 elbindingvnode 这三个参数。

oldVnode 只有在 update 与 componentUpdated 钩子中生效。

参数el 就是指令绑定的 DOM 元素,而binding是一个对象,它包含一下属性:namevalueoldValueexpressionargmodifiers

另外值得注意的一点是,除了 el 之外,bindingvnode 属性都是只读的。

熟悉指令的创建方式与参数之后,我们就用它来创建一个案例。

创建自定义指令

Loading 是项目中最常见的一个小功能,别看它功能小,但是起到的作用却很大,手动创建一个 Loading 指令。

Vue.directive("loading", {bind(el, binding) {if (binding.value) {let div = document.createElement("div");div.className = "loading-parent";div.innerHTML = `<div class="loading-spinner"><i class='el-icon-loading'></i></div>`;el.appendChild(div);}},update(el, binding) {if (binding.value) {let div = document.createElement("div");div.className = "loading-parent";div.innerHTML = `<div class="loading-spinner"><i class='el-icon-loading'></i></div>`;el.appendChild(div);el.load = div;} else {el.load && el.load.parentNode && el.load.parentNode.removeChild(el.load);}}
});

然后我们直接就可以在模版中使用了。

<div v-loading="loading" class="box"></div>

你也看出来了很多代码是重复的,怎么办呢?

Vue 中给我们提供了简写方式。当只有这两个钩子函数时bindupdate,我们可以简写如下。

Vue.directive("loading", function(el, binding) {if (binding.value) {let div = document.createElement("div");div.className = "loading-parent";div.innerHTML = `<div class="loading-spinner"><i class='el-icon-loading'></i></div>`;el.appendChild(div);el.load = div;} else {el.load && el.load.parentNode && el.load.parentNode.removeChild(el.load);}
});

我们实现了一个非常简单的指令,但还不够灵活,比如我想添加 loading 的背景色,修改图标的颜色怎么办呢?

这时候就需要给指令传入多个值,改造下实现背景与图标颜色。

Vue.directive("loading", function(el, binding) {if (binding.value) {let div = document.createElement("div");div.className = "loading-parent";div.style.backgroundColor = binding.value.background;div.style.color = binding.value.color;div.innerHTML = `<div class="loading-spinner"><i class='el-icon-loading'></i></div>`;el.appendChild(div);el.load = div;} else {el.load && el.load.parentNode && el.load.parentNode.removeChild(el.load);}
});

直接使用

<div v-loading="{color: 'white', background: '#000'}">我可以拥有更多属性</div>

在文章开始我们介绍指令时,还说到指令的「参数」与「修饰符」这里我就不多介绍了,大家不妨自己去尝试一下吧。体验下自定义指令的魅力。

29、Vue 中怎么自定义过滤器

注意事项:

(1)全局方法 Vue.filter() 注册一个自定义过滤器,必须放在Vue实例化前面

(2) 过滤器函数始终以表达式的值作为第一个参数。带引号的参数视为字符串,而不带引号的参数按表达式计算

(3)可以设置两个过滤器参数,前提是这两个过滤器处理的不冲突

(4)用户从input输入的数据在回传到model之前也可以先处理

复制代码

<!DOCTYPE html>
<html><head><meta charset="UTF-8"><title>vue自定义过滤器</title><script src="../js/vue.js" type="text/javascript" charset="utf-8"></script><meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" /></head><body><div class="test"><p>{{message | sum}}</p><p>{{message | cal 10 20}}</p>  <!--过滤器函数始终以表达式的值作为第一个参数。带引号的参数视为字符串,而不带引号的参数按表达式计算。--><p>{{message | sum | currency }}</p> <!--添加两个过滤器,注意不要冲突--><input type="text" v-model="message | change"> <!--用户从input输入的数据在回传到model之前也可以先处理--></div><script type="text/javascript">//        -----------------------------------------华丽分割线(从model->view)---------------------------------------Vue.filter("sum", function(value) {   //全局方法 Vue.filter() 注册一个自定义过滤器,必须放在Vue实例化前面return value + 4;});Vue.filter('cal', function (value, begin, xing) {   //全局方法 Vue.filter() 注册一个自定义过滤器,必须放在Vue实例化前面return value + begin + xing;});
​
//        -----------------------------------------华丽分割线(从view->model)---------------------------------------Vue.filter("change", {read: function (value) { // model -> view 在更新 `<input>` 元素之前格式化值return value;},write: function (newVal,oldVal) { // view -> model  在写回数据之前格式化值console.log("newVal:"+newVal); console.log("oldVal:"+oldVal);return newVal;}});
​var myVue = new Vue({el: ".test",data: {message:12}});</script></body>
</html>

复制代码

二 自定义过滤器(注册在实例化内部)

上面的例子直接注册在Vue全局上面,其他不用这个过滤器的实例也会被迫接受,其实过滤器可以注册在实例内部,仅在使用它的实例里面注册

上面的程序改写为:

复制代码

<!DOCTYPE html>
<html><head><meta charset="UTF-8"><title>vue自定义过滤器</title><script src="../js/vue.js" type="text/javascript" charset="utf-8"></script><meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" /></head><body><div class="test"><p>{{message | sum}}</p><p>{{message | cal 10 20}}</p>  <!--过滤器函数始终以表达式的值作为第一个参数。带引号的参数视为字符串,而不带引号的参数按表达式计算。--><p>{{message | sum | currency }}</p> <!--添加两个过滤器,注意不要冲突--><input type="text" v-model="message | change"> <!--用户从input输入的数据在回传到model之前也可以先处理--></div><script type="text/javascript">Vue.filter("change", {read: function (value) { // model -> view 在更新 `<input>` 元素之前格式化值return value;},write: function (newVal,oldVal) { // view -> model  在写回数据之前格式化值console.log("newVal:"+newVal); console.log("oldVal:"+oldVal);return newVal;}});
​var myVue = new Vue({el: ".test",data: {message:12},filters: {sum: function (value) {return value + 4;},cal: function (value, begin, xing) {return value + begin + xing;}}});</script></body>
</html>

30、Vue 中 key 的作用

1.v-if中用key管理可复用的元素

  Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染。这么做,除了使 Vue 变得非常快之外,还有一些有用的好处。例如,如果你允许用户在不同的登录方式之间切换:

img

那么在上面的代码中切换 loginType 将不会清除用户已经输入的内容。因为两个模版使用了相同的元素,<input> 不会被替换掉——仅仅是替换了它的 placeholder。这样也不总是符合实际需求,所以 Vue 为你提供了一种方式来声明“这两个元素是完全独立的——不要复用它们”。只需添加一个具有唯一值的 key 属性即可:

img

2、v-for中的key

  当 Vue.js 用 v-for 正在更新已渲染过的元素列表时,它默认用 “就地复用” 策略。如果数据项的顺序被改变,Vue将不是移动 DOM 元素来匹配数据项的顺序, 而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素。这个类似 Vue 1.x 的 track-by="$index" 。

  这个默认的模式是有效的,但是只适用于不依赖子组件状态或临时 DOM 状态(例如:表单输入值)的列表渲染输出。

  为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一 key 属性。理想的 key 值是每项都有唯一 id。这个特殊的属性相当于 Vue 1.x 的 track-by ,但它的工作方式类似于一个属性,所以你需要用 v-bind 来绑定动态值(在这里使用简写):

img

31、Vue 的核心思想是什么

Vue核心思想:数据驱动、组件化

一,数据驱动

传统的前端数据交互是用Ajax从服务端获取数据,然后操作DOM来改变视图;或者前端交互要改变数据时,又要再来一次上述步骤,而手动操作DOM是一个繁琐的过程且易出错。 Vue.js 是一个提供了 MVVM 风格的双向数据绑定的 Javascript 库,专注于View 层。它让开发者省去了操作DOM的过程,只需要改变数据。Vue会通过Dircetives指令,对DOM做一层封装,当数据发生改变会通知指令去修改对应的DOM,数据驱动DOM变化,DOM是数据的一种自然映射。 Vue还会对操作进行监听,当视图发生改变时,vue监听到这些变化,从而改变数据,这样就形成了数据的双向绑定。 Vue是一种MVVM框架。而DOM是数据的一个种自然映射。传统的模式是通过Ajax请求从model请求数据,然后手动的触发DOM传入数据修改页面。Vue中,Directives对view进行了封装,当model里的数据发生变化是,Vue就会通过Directives指令去修改DOM。同时也通过DOM Listener实现对视图view的监听,当DOM改变时,就会被监听到,实现model的改变,实现数据的双向绑定。

二,组件响应原理 数据(model)改变驱动视图(view)自动更新 在这里插入图片描述 当你把一个普通的 JavaScript 对象传给 Vue 实例的 data选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty 把这些属性全部转为 getter/setter。Object.defineProperty 是 ES5 中一个无法 shim 的特性,这也就是为什么 Vue 不支持 IE8 以及更低版本浏览器的原因。 用户看不到 getter/setter,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化。这里需要注意的问题是浏览器控制台在打印数据对象时 getter/setter 的格式化并不同,所以你可能需要安装 vue-devtools 来获取更加友好的检查接口。 每个组件实例都有相应的 watcher 实例对象,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的setter被调用时,会通知watcher重新计算,从而致使它关联的组件得以更新。

三,组件化

扩展HTML元素,封装可重用的代码。每一个组件都对应一个ViewModel。页面上每个独立的可视/可交互区域都可以视为一个组件。每个组件对应一个工程目录,组件所需要的各种资源在这个目录下就进维护。页面是组件的容器,组件可以嵌套自由组合形成完整的页面。

组件化实现了扩展HTML元素,封装可用的代码。页面上每个独立的可视/可交互区域视为一个组件;每个组件对应一个工程目录,组件所需要的各种资源在这个目录下就近维护;页面不过是组件的容器,组件可以嵌套自由组合形成完整的页面。

32、Vue 等单页面应用的优缺点

先来说说什么是单页面应用和多页面应用:

单页面应用(SPA),通俗一点说就是指只有一个主页面的应用,浏览器一开始要加载所有必须的 html, js, css。所有的页面内容都包含在这个所谓的主页面中。

多页面(MPA),就是指一个应用中有多个页面,页面跳转时是整页刷新。

单页面的优点和缺点:

优点:

1、用户体验好,快,内容的改变不需要重新加载整个页面,对服务器压力较小。

2、前后端分离,比如vue项目

3、完全的前端组件化,前端开发不再以页面为单位,更多地采用组件化的思想,代码结构和组织方式更加规范化,便于修改 和调整;

缺点:

1、首次加载页面的时候需要加载大量的静态资源,这个加载时间相对比较长。

2、不利于 SEO优化,单页页面,数据在前端渲染,就意味着没有 SEO。

3、页面导航不可用,如果一定要导航需要自行实现前进、后退。(由于是单页面不能用浏览器的前进后退功能,所以需要自 己建立堆栈管理)

33、Vue-router 使用params与query传参有什么区别

首先简单来说明一下$router$route的区别

//$router : 是路由操作对象,只写对象
//$route : 路由信息对象,只读对象
​
//操作 路由跳转
this.$router.push({name:'hello',params:{name:'word',age:'11'}
})
​
//读取 路由参数接收
this.name = this.$route.params.name;
this.age = this.$route.params.age;123456789101112131415

1·query传递参数

我看了很多人都说query传参要用path来引入,params传参要用name来引入,只是我测试了一下,query使用name来引入也可以传参,使用path也可以。如果有人知道原因可以告诉我一下,谢谢!

//query传参,使用name跳转
this.$router.push({name:'second',query: {queryId:'20180822',queryName: 'query'}
})
​
//query传参,使用path跳转
this.$router.push({path:'second',query: {queryId:'20180822',queryName: 'query'}
})
​
//query传参接收
this.queryName = this.$route.query.queryName;
this.queryId = this.$route.query.queryId;123456789101112131415161718192021

这里写图片描述

最终不管是path引入还是name引入效果都一样如下图 这里写图片描述

2·params传递参数

注:使用params传参只能使用name进行引入

  • 使用params传参

//params传参 使用name
this.$router.push({name:'second',params: {id:'20180822',name: 'query'}
})
​
//params接收参数
this.id = this.$route.params.id ;
this.name = this.$route.params.name ;
​
//路由
​
{
path: '/second/:id/:name',
name: 'second',
component: () => import('@/view/second')
}1234567891011121314151617181920

这里写图片描述

效果如下图 这里写图片描述

需要注意的是:

  1. params是路由的一部分,必须要在路由后面添加参数名。query是拼接在url后面的参数,没有也没关系。

  2. params一旦设置在路由,params就是路由的一部分,如果这个路由有params传参,但是在跳转的时候没有传这个参数,会导致跳转失败或者页面会没有内容。

如果路由后面没有 /:id/:name效果如下图,地址栏没有参数 这里写图片描述

但是如果你刷新一下,就会发现页面失败,效果如下图 这里写图片描述

因此我们不可能让用户不要刷新,所以我们必须在路由后面加上 /:id/:name

  • 如果使用path进行传参

//params传参 使用path
this.$router.push({path:'second',params: {id:'20180822',name: 'query'}
})
​
//params接收参数
this.id = this.$route.params.id ;
this.name = this.$route.params.name ;123456789101112

效果如下图

使用path传参什么效果都没有。

这里写图片描述

3.总结

  1. 传参可以使用params和query两种方式。

  2. 使用params传参只能用name来引入路由,即push里面只能是name:’xxxx’,不能是path:’/xxx’,因为params只能用name来引入路由,如果这里写成了path,接收参数页面会是undefined!!!。

  3. 使用query传参使用path来引入路由。

  4. params是路由的一部分,必须要在路由后面添加参数名。query是拼接在url后面的参数,没有也没关系。

  5. 二者还有点区别,直白的来说query相当于get请求,页面跳转的时候,可以在地址栏看到请求参数,而params相当于post请求,参数不会再地址栏中显示。

34、子组件像父组件传递事件?

$emit方法

35、Vue中 <keep-alive> 的作用

1.keep-alive的作用以及好处

在做电商有关的项目中,当我们第一次进入列表页需要请求一下数据,当我从列表页进入详情页,详情页不缓存也需要请求下数据,然后返回列表页,这时候我们使用keep-alive来缓存组件,防止二次渲染,这样会大大的节省性能。

2.keep-alive的基本用法

img

需要缓存的组件内容直接在router中添加:

img

3.keep-alive的生命周期

当引入keep-alive的时候,页面第一次进入,钩子的触发顺序created-> mounted-> activated,退出时触发deactivated。当再次进入(前进或者后退)时,只触发activated。

36、如何让CSS只在当前组件中起作用?

在组件中的style前面加上scoped

37、如何获取dom?

ref="domName" 用法:this.$refs.domName

38、说出几种vue当中的指令和它的用法?

:v-model双向数据绑定; v-for循环; v-if v-show 显示与隐藏; v-on事件;v-once: 只绑定一次。

39、Vue Router的钩子函数有哪些?他们有哪些参数?

全局的:beforeEach、beforeResolve、afterEach 路由的:beforeEnter 组件的:beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave 参数:to、from、next;正对不同的钩子函数参数有所差异。

40、说明一下封装vue组件的原则和方法

页面尽量不要封装为组件,只将一些复用率高的部分封装为组件就行。例如新增或修改信息的弹框,七八个页面都要用,自然有理由封装为组件。

并且要高内聚低耦合,说白了万万不可依赖于某一个或某几个页面的逻辑

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/453033.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何用VS实现动态爱心

首先下载一个easyx库 其次输入以下代码&#xff1a; 代码1 //#define _CRT_SECURE_NO_WARNINGS 1#include<easyx.h>//图形库 #include<stdio.h> #include<time.h> #include<math.h>//定义一个结构体 struct point {double x, y;COLORREF color; };COL…

瀚海微SD NAND存储功能描述(15)命令类b

1)传输的数据不得跨越物理块边界&#xff0c;除非在CSD中设置了WRITE BLK MISALIGN。如果不支持写部分块&#xff0c;则块长度-默认块长度(在CSD中给出)1 2) SDSC卡(CCS0)使用字节单位地址&#xff0c;SDHC和SDXC卡(CCS1)使用块单位地址(512字节单位)。 1) 32个写保护位(代表…

汽车行业焕新潮流涌动,联众优车以优质服务响应市场变化

随着消费者环保意识的改变及新能源汽车市场的快速发展&#xff0c;我国新能源汽车领域正掀起一股新的消费热潮&#xff0c;而旧车的合理处置问题也随之成为社会各界关注的焦点。今年4月末&#xff0c;商务部、财政部等七大部委携手颁布了《老旧汽车置换补贴实施指南》(以下简称…

Maven--简略

简介 Apache旗下的一款开源项目&#xff0c;用来进行项目构建&#xff0c;帮助开发者管理项目中的jar及jar包之间的依赖&#xff0c;还拥有项目编译、测试、打包的功能。 管理方式 统一建立一个jar仓库&#xff0c;把jar上传至统一的仓库&#xff0c;使用时&#xff0c;配置…

深入理解MySQL InnoDB中的B+索引机制

目录 一、InnoDB中的B 树索引介绍 二、聚簇索引 &#xff08;一&#xff09;使用记录主键值的大小进行排序 页内记录排序 页之间的排序 目录项页的排序 &#xff08;二&#xff09;叶子节点存储完整的用户记录 数据即索引 自动创建 &#xff08;三&#xff09;聚簇索引…

[ES3]大侠立志传存档解密修改

找到存档位置&#xff0c;如果是PC端用户&#xff1a;C:\Users\你自己的用户名\AppData\LocalLow\DefaultCompany\Wulin\一串steamID\选择你要改的存档 这里你要改的存档如果是AutoSave就是自动保存&#xff0c;如果是Save加序号就是你手动保存的存档。 手机端用户自行查其他资…

模拟键盘输入卡号RFID读卡器银河麒麟桌面操作系统兼容适配认证测试报告

本测试报告使用读卡器&#xff1a;https://item.taobao.com/item.htm?spma21dvs.23580594.0.0.1d292c1b72i5j0&ftt&id702441469725

通过无线路由器连接三菱PLC的设置方法

1.首先设置无线路由器上网方式为DHCP&#xff08;自动获取IP地址&#xff09;。点击保存&#xff0c;然后点击更多功能 2.再点击网络设置-局域网&#xff0c;勾选DHCP服务器&#xff0c;此功能的作用是对局域网内所有设备分配IP地址。 然后保存&#xff1b; 3.再点击系统设置…

【论文笔记】Fine-tuned CLIP Models are Efficient Video Learners

&#x1f34e;个人主页&#xff1a;小嗷犬的个人主页 &#x1f34a;个人网站&#xff1a;小嗷犬的技术小站 &#x1f96d;个人信条&#xff1a;为天地立心&#xff0c;为生民立命&#xff0c;为往圣继绝学&#xff0c;为万世开太平。 基本信息 标题: Fine-tuned CLIP Models a…

前端一键复制解决方案分享

需求背景 用户需要对流水号进行复制使用&#xff0c;前端的展示是通过样式控制&#xff0c;超出省略号表示&#xff0c;鼠标悬浮展示完整流水号。此处的鼠标悬浮展示采用的是:title&#xff0c;这样就无法对文本进行选中。 下面是给出一键复制的不同的解决方案&#xff0c;希望…

Flink本地安装

去官网下载安装包Index of /apache/flink/flink-1.17.2 进行解压 tar -zxvf /opt/install_packages/flink-1.17.2-bin-scala_2.12.tgz 解压完成后进入flink-1.17目录&#xff0c;执行以下命令启动flink服务 ./bin/start-cluster.sh 执行成功 之后尝试打开flink的webuihttp:/…

qtcreator 仿制vscode黑色背景主题monokai

qtcreator 仿制vscode的monokai黑色主题 1.演示 ​​ ​​ 2.主题配置 ​​ ​​ ​​ ​​ ​​ ​​ ​​ 删掉所有的 把我下面的复制进去 <?xml version"1.0" encoding"UTF-8"?> <style-scheme version"1.0" name&qu…

深入解析大模型RAG:检索、增强与生成的全面指南

RAG&#xff08;Retrieval-Augmented Generation&#xff0c;检索增强生成&#xff09; 是一种结合了信息检索技术与语言生成模型的人工智能技术。该技术通过从外部知识库中检索相关信息&#xff0c;并将其作为提示&#xff08;Prompt&#xff09;输入给大型语言模型&#xff0…

哈工深、微信:“慢思考”超长文档翻译智能体

如今&#xff0c;大语言模型已经成为机器翻译任务&#xff08;Machine Translation&#xff09;上的新型强大工具。 然而&#xff0c;多数在机器翻译大语言模型&#xff08;MT-LLM&#xff09;上开展的研究工作都是句子层面的&#xff0c;每一句话都被独立进行翻译&#xff0c;…

训练VLM(视觉语言模型)的经验

知乎&#xff1a;lym 链接&#xff1a;https://zhuanlan.zhihu.com/p/890327005 如果可以用prompt解决&#xff0c;尽量用prompt解决&#xff0c;因为训练&#xff08;精调&#xff09;的模型往往通用能力会下降&#xff0c;训练和长期部署成本都比较高&#xff0c;这个成本也包…

vScode---配置Pyqt5环境--记录

前提条件&#xff1a; python运行环境已正常安装 1、安装Pyqt5 第三方库下载地址记录&#xff1a; 清华大学&#xff1a;https://pypi.tuna.tsinghua.edu.cn/simple 豆瓣&#xff1a;https://pypi.douban.com/simple 阿里云&#xff1a;https://mirrors.aliyun.com/pypi/simpl…

一文读懂组态图和组态软件,最浅显的解读

一、什么是组态图 组态图是指在工业自动化领域中&#xff0c;用来描述和展示控制系统中各个组件之间关系和工作流程的图形化表示方法。它是一个系统的框架图&#xff0c;通过图形符号和连接线&#xff0c;将各个组件&#xff08;如传感器、执行器、控制器等&#xff09;以及它…

linux ps和kill指令

目录 ps 命令 kill指令&#xff1a; 示例&#xff1a; 补充&#xff1a;管道的概念 管道的概念 管道的用途 示例 在Linux系统中&#xff0c;ps 和 kill 是两个非常常用的命令&#xff0c;用于管理和终止进程。 ps 命令 ps 命令用于显示当前系统中的进程状态。它可以提供…

责任链模式下,解决开闭原则问题实践

前言 在现代软件工程中&#xff0c;设计模式是解决常见问题的有效工具之一。它们吸收了前人的经验&#xff0c;不仅帮助开发者编写更清晰、更可维护的代码&#xff0c;还能促进团队之间的沟通和协作。责任链模式&#xff08;Chain of Responsibility Pattern&#xff09;作为一…

无人机+视频推流直播EasyCVR视频汇聚/EasyDSS平台在森林防护巡检中的解决方案

随着科技的飞速发展&#xff0c;无人机技术在各个领域的应用日益广泛&#xff0c;特别是在森林防护与巡检方面&#xff0c;无人机以其独特的优势&#xff0c;为传统林业管理带来了革命性的变化。本文将探讨无人机在森林防护巡检中的解决方案&#xff0c;分析其工作原理、优势及…