vue综合指南(二)

news2024/10/20 2:58:46

​🌈个人主页:前端青山
🔥系列专栏: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.coloradmin.cn/o/2218951.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

sql高级

数据库的范式 为了建立冗余较小、结构合理的数据库&#xff0c;设计数据库时必须遵循一定的规则。在关系型数据库中这种规则就称为范式。 范式是符合某一种设计要求的总结。要想设计一个结构合理的关系型数据库&#xff0c;必须满足一定的范式。实际上&#xff0c;数据库范式…

基于SSM+微信小程序的打印室预约管理系统(打印2)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1、项目介绍 基于SSM微信小程序的打印室预约管理系统实现了管理员和用户两个角色。 1、管理员功能有个人中心&#xff0c;用户管理&#xff0c;附近打印店管理&#xff0c;文件打印管理&#xff0c;当…

iTOP-RK3568开发板独立NPU通过算法加特应用到以下的场景

iTOP-3568开发板采用瑞芯微RK3568处理器&#xff0c;内部集成了四核64位Cortex-A55处理器。主频高达2.0Ghz&#xff0c;RK809动态调频。集成了双核心架构GPU&#xff0c;ARM G52 2EE、支持OpenGLES1.1/2.0/3.2、OpenCL2.0、Vulkan1.1、内嵌高性能2D加速硬件。 内置独立NPU,算力…

Yocto - 使用Yocto开发嵌入式Linux系统_10 使用Yocto项目进行开发

Developing with the Yocto Project 在本书中&#xff0c;我们一直将 Poky 用作构建工具。换句话说&#xff0c;我们将其用作设计和生成镜像的工具&#xff0c;将镜像交付到产品。 So far in this book, we have used Poky as a build tool. In other words, we have used it a…

Nature 正刊丨核糖体如何塑造蛋白质折叠

01摘要 涉及人工智能的方法彻底改变了从氨基酸序列预测蛋白质结构的能力。然而&#xff0c;这些结构通过在细胞内折叠形成的过程仍然大多难以捉摸——尽管正确折叠对蛋白质功能的重要性&#xff0c;以及折叠失败与许多毁灭性疾病之间的联系。大多数蛋白质只有在核糖体&#xf…

鸿蒙网络编程系列23-实现一个基于鸿蒙API的HTTP服务器

1. 鸿蒙HTTP服务器可行性分析 看到这个题目&#xff0c;可能有的小伙伴会有一些疑问&#xff1a; “鸿蒙API从9到12我都翻烂了&#xff0c;也没见提供HTTP服务器的接口啊” “我BUG写得少&#xff0c;你可不要骗我” 的确&#xff0c;目前的鸿蒙API没有提供HTTP服务端的实现…

【每日一题】【算法双周赛】【第 20 场 小白入门赛评价/分享】赛后另类AI写题分析分享

第 20 场 小白入门赛 1. 四个亲戚【算法赛】2. 黛玉泡茶【算法赛】AI分析具体实现代码解析复杂度分析示例运行 结果二 3. 宝玉请安【算法赛】AI分析问题分析路径计算代码实现代码解析示例运行复杂度分析 结果&#xff1a; 交上去 4. 贾母祝寿【算法赛】AI分析问题分析实现步骤代…

【日志】关于多益网申

2024.10.19 早先听闻多益的测试题非常抽象&#xff0c;凡是测过的人都说太抽象了&#xff0c;我还以为他考我各种算法或者编程语言呢。我今天也去做了一下&#xff0c;测试题里面大多都考些计算题&#xff0c;找规律题&#xff0c;判断推理题&#xff0c;还有一些图形转换&…

计算机毕业设计 基于java旅游攻略平台的设计与实现 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

【命令操作】Linux中多种关机和重启的命令介绍 _ 统信 _ 麒麟 _ 方德

往期好文&#xff1a;【系统配置】信创系统配置文件保护与防篡改 | 统信 | 麒麟 | 方德 Hello&#xff0c;大家好啊&#xff01;今天给大家带来一篇关于在Linux系统中多种关机和重启命令介绍的文章。Linux作为一个广泛应用的开源操作系统&#xff0c;提供了多种关机和重启的方式…

jupyter notebook 笔记

nbclassic 经典版 新版的 jupyter notebook 太丑了。 最难受的是字体太小了。 我还是喜欢老版本的 jupyter notebook. 安装经典版: pip install nbclassic 启动经典版: jupyter server 或是 jupyter nbclassic 参考来源: https://github.com/jupyter/nbclassic jupyter note…

计算机网络——传输层服务

传输层会给段加上目标ip和目标端口号 应用层去识别报文的开始和结束

C++ —— 关于多态

目录 1. 多态的概念 2.多态的定义及实现 3. 虚函数 3.1 虚函数的重写/覆盖 3.2 关于多态的面试难题 3.3 虚函数重写的⼀些问题 3.4 override 和 final关键字 3.5 重载/重写/隐藏的对比 3.6 纯虚函数和抽象类 4.多态的原理 4.1虚函数表指针 4.2 多态是如何实现的 4…

go压缩的使用

基础&#xff1a;使用go创建一个zip func base(path string) {// 创建 zip 文件zipFile, err : os.Create("test.zip")if err ! nil {panic(err)}defer zipFile.Close()// 创建一个新的 *Writer 对象zipWriter : zip.NewWriter(zipFile)defer zipWriter.Close()// 创…

如何使用DockerSpy检测你的Docker镜像是否安全

关于DockerSpy DockerSpy是一款针对Docker镜像的敏感信息检测与安全审计工具&#xff0c;该工具可以帮助广大研究人员在Docker Hub上检测和搜索自己镜像的安全问题&#xff0c;并识别潜在的泄漏内容&#xff0c;例如身份验证密钥等敏感信息。 功能介绍 1、安全审计&#xff1a…

linux一二三章那些是重点呢

第一章 静态库动态库的区别 什么是库 库文件是计算机上的一类文件&#xff0c;可以简单的把库文件看成一种代码仓库&#xff0c;它提供给使用者一些可以直接 拿来用的变量、函数或类。 如何制作 静态动态库 静态库&#xff1a; GCC 进行链接时&#xff0c;会把静态库中代码打…

2013 lost connection to MySQL server during query

1.问题 使用navicat连接doris&#xff0c;会有这个错误。 2.解决 换低版本的navicat比如navicat11。

linux运行openfoam并行会报错:attempt to run parallel on 1 processor

linux运行openfoam并行会报错&#xff1a;attempt to run parallel on 1 processor 步骤&#xff1a; 1.先在终端输入which mpirun,查看当前并行路径&#xff1b; 2.输入gedit ~/.bashrc&#xff0c;文本方式打开bashrc文件&#xff1b; 3.修改为export PATH/usr/bin:$PATH&am…

支持阅后即焚的笔记Enclosed

什么是 Enclosed &#xff1f; Enclosed 是一个简约的网络应用程序&#xff0c;旨在发送私人和安全的笔记。所有笔记均经过端到端加密&#xff0c;确保服务器和存储对内容一无所知。用户可以设置密码、定义有效期 (TTL)&#xff0c;并选择在阅读后让笔记自毁。 软件特点&#x…

第一年改考408的学校有炸过的吗?怎么应对突然改考408?

C哥专业提供——计软考研院校选择分析专业课备考指南规划 专业课改考 408 后&#xff0c;分数线不一定会暴涨&#xff0c;其变化受到多种因素影响&#xff1a; 可能导致分数线不暴涨甚至下降的因素&#xff1a; 考试难度增加&#xff1a;408 统考涵盖数据结构、计算机组成原理…