当前位置: 首页 > news >正文

贵州定制型网站建设电脑培训学校学费多少

贵州定制型网站建设,电脑培训学校学费多少,安装wordpress模板,医院网站建设 招标给大家推荐一个实用面试题库 1、前端面试题库 #xff08;面试必备#xff09; 推荐#xff1a;★★★★★ 地址#xff1a;web前端面试题库 很喜欢‘万变不离其宗’这句话#xff0c;希望在不断的思考和总结中找到Vue中的宗#xff0c;来解答面试官抛出的… 给大家推荐一个实用面试题库 1、前端面试题库 面试必备            推荐★★★★★ 地址web前端面试题库 很喜欢‘万变不离其宗’这句话希望在不断的思考和总结中找到Vue中的宗来解答面试官抛出的各种Vue问题一起加油~ 一、MVVM原理 在Vue2官方文档中没有找到Vue是MVVM的直接证据但文档有提到虽然没有完全遵循MVVM模型但是 Vue 的设计也受到了它的启发因此在文档中经常会使用vm(ViewModel 的缩写) 这个变量名表示 Vue 实例。 为了感受MVVM模型的启发我简单列举下其概念。 MVVM是Model-View-ViewModel的简写由三部分构成 Model: 模型持有所有的数据、状态和程序逻辑View: 负责界面的布局和显示ViewModel负责模型和界面之间的交互是Model和View的桥梁 二、SPA单页面应用 单页Web应用single page web applicationSPA就是只有一张Web页面的应用是加载单个HTML页面并在用户与应用程序交互时动态更新该页面的Web应用程序。我们开发的Vue项目大多是借助个官方的CLI脚手架快速搭建项目直接通过new Vue构建一个实例并将el:#app挂载参数传入最后通过npm run build的方式打包后生成一个index.html称这种只有一个HTML的页面为单页面应用。 当然vue也可以像jq一样引入作为多页面应用的基础框架。 三、Vue的特点 清晰的官方文档和好用的api比较容易上手。是一套用于构建用户界面的渐进式框架将注意力集中保持在核心库而将其他功能如路由和全局状态管理交给相关的库。使用 Virtual DOM。提供了响应式 (Reactive) 和组件化 (Composable) 的视图组件。 四、Vue的构建入口 vue使用过程中可以采用以下两种方式 在vue脚手架中直接使用参考文档https://cn.vuejs.org/v2/guide/installation.html或者在html文件的头部通过静态文件的方式引入 script srchttps://cdn.jsdelivr.net/npm/vue2.6.14/dist/vue.js/script 那么问题来了使用的或者引入的到底是什么 答引入的是已经打包好的vue.js文件通过rollup构建打包所得。 构建入口在哪里 答在vue源码的package.json文件中 scripts: {// ...build: node scripts/build.js,build:ssr: npm run build -- web-runtime-cjs,web-server-renderer,build:weex: npm run build -- weex,// ...},通过执行npm run build的时候会进行scripts/build.js文件的执行npm run buildssr和npm run buildweex的时候将ssr和weex作为参数传入按照参数构建出不一样的vue.js打包文件。 所以说vue中的package.json文件就是构建的入口具体构建流程可以参考vue2入口构建入口。 五、对import Vue from vue的理解 在使用脚手架开发项目时会有一行代码import Vue from vue那么这个Vue指的是什么。 答一个构造函数。 function Vue (options) {if (process.env.NODE_ENV ! production !(this instanceof Vue)) {warn(Vue is a constructor and should be called with the new keyword)}this._init(options) } initMixin(Vue) stateMixin(Vue) eventsMixin(Vue) lifecycleMixin(Vue) renderMixin(Vue)我们开发中引入的Vue其实就是这个构造函数而且这个构造函数只能通过new Vue的方式进行使用否则会在控制台打印警告信息。定义完后还会通过initMixin(Vue)、stateMixin(Vue)、eventsMixin(Vue)、lifecycleMixin(Vue)和renderMixin(Vue)的方式为Vue原型中混入方法。我们通过import Vue from Vue引入的本质上就是一个原型上挂在了好多方法的构造函数。 六、对new Vue的理解 // main.js文件 import Vue from vue; var app new Vue({el: #app,data() {return {msg: hello Vue~}},template: div{{msg}}/div, })console.log(app);new Vue就是对构造函数Vue进行实例化执行结果如下 可以看出实例化后的实例中包含了很多属性用来对当前app进行描述当然复杂的Vue项目这个app将会是一个树结构通过$parent和$children维护父子关系。 new Vue的过程中还会执行this._init方法进行初始化处理。 七、编译 虚拟DOM的生成必须通过render函数实现render函数的产生是在编译阶段完成核心代码如下 export const createCompiler createCompilerCreator(function baseCompile (template: string,options: CompilerOptions ): CompiledResult {const ast parse(template.trim(), options)if (options.optimize ! false) {optimize(ast, options)}const code generate(ast, options)return {ast,render: code.render,staticRenderFns: code.staticRenderFns} })主要完成的功能是 通过const ast parse(template.trim(), options)将template转换成ast树通过optimize(ast, options)对ast进行优化通过const code generate(ast, options)将优化后的ast转换成包含render字符串的code对象最终render字符串通过new Function转换为可执行的render函数 模板编译的真实入口可以参考vue2从template到render模板编译入口parse可以参考vue2从template到renderASToptimize可以参考vue2从template到renderoptimizegenerate可以参考vue2从template到rendercode 八、虚拟DOM 先看浏览器对HTML的理解 div h1My title/h1 Some text content !-- TODO: Add tagline -- /div当浏览器读到这些代码时它会建立一个DOM树来保持追踪所有内容如同你会画一张家谱树来追踪家庭成员的发展一样。 上述 HTML 对应的 DOM 节点树如下图所示 每个元素都是一个节点。每段文字也是一个节点。甚至注释也都是节点。一个节点就是页面的一个部分。就像家谱树一样每个节点都可以有孩子节点 (也就是说每个部分可以包含其它的一些部分)。 再看Vue对HTML template的理解 Vue 通过建立一个虚拟 DOM 来追踪自己要如何改变真实 DOM。因为它所包含的信息会告诉 Vue 页面上需要渲染什么样的节点包括及其子节点的描述信息。我们把这样的节点描述为“虚拟节点 (virtual node)”也常简写它为“VNode”。“虚拟 DOM”是我们对由 Vue 组件树建立起来的整个 VNode 树的称呼。 简言之浏览器对HTML的理解是DOM树Vue对HTML的理解是虚拟DOM最后在patch阶段通过DOM操作的api将其渲染成真实的DOM节点。 九、模板或者组件渲染 Vue中的编译会执行到逻辑vm._update(vm._render(), hydrating)其中的vm._render执行会获取到vNodevm._update就会对vNode进行patch的处理又分为模板渲染和组件渲染。 十、数据响应式处理 Vue的数据响应式处理的核心是Object.defineProperty在递归响应式处理对象的过程中为每一个属性定义了一个发布者dep当进行_render函数执行时会访问到当前值在get中通过dep.depend进行当前Watcher的收集当数据发生变化时会在set中通过dep.notify进行Watcher的更新。 数据响应式处理以及发布订阅者模式的关系请参考vue2从数据变化到视图变化发布订阅模式 十一、this.$set const app new Vue({el: #app,data() {return {obj: {name: name-1}};},template: div clickchange{{obj.name}}的年龄是{{obj.age}}/div,methods: {change() {this.obj.name name-2;this.obj.age 30;}} });以上例子执行的结果是 name-1的年龄是 当点击后依然是 name-2的年龄是 可以看出点击后obj的name属性变化得到了视图更新而age属性并未进行变化。 name属性响应式的过程中锁定了一个发布者dep在当前视图渲染时在发布者dep的subs中做了记录一旦其发生改变就会触发set方法中的dep.notify继而执行视图的重新渲染。然而age属性并未进行响应式的处理当其改变时就不能进行视图渲染。 十二、组件注册 组件的使用是先注册后使用又分为 全局注册可以直接在页面中使用局部注册使用时需要通过import xxx from xxx的方式引入并且在当前组件的选项components中增加局部组件的名称。 十三、异步组件 Vue单页面应用中一个页面只有一个div idapp/div承载所有节点因此复杂项目可能会出现首屏加载白屏等问题Vue异步组件就很好的处理了这问题。 十四、this.$nextTick 因为通过new实例化构造函数Vue的时候会执行初始化方法this._init其中涉及到的方法大多都是同步执行。nextTick在vue中是一个很重要的方法在new Vue实例化的同步过程中将一些需要异步处理的函数推到异步队列中去可以等new Vue所有的同步任务执行完后再执行异步队列中的函数。 nextTick的实现可以参考 vue2从数据变化到视图变化nextTick 十五、keep-alive内置组件 vue中支持组件化并且也有用于缓存的内置组件keep-alive可直接使用使用场景为路由组件和动态组件。 activated表示进入组件的生命周期deactivated表示离开组件的生命周期include表示匹配到的才缓存exclude表示匹配到的都不缓存max表示最多可以缓存多少组件 keep-alive的具体实现请参考 vue中的keep-alive源码分析 十六、生命周期 vue中的生命周期有哪些 答案11个分别为beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、activated、deactivated、beforeDestroy、destroyed和errorCaptured。 具体实现请参考 vue生命周期 十七、v-show和v-if的区别 先看v-if和v-show的使用场景 1v-if更多的使用在需要考虑白屏时间或者切换次数很少的场景 2v-show更多使用在transition控制的动画或者需要非常频繁地切换的场景 再从底层实现思路上分析 1v-if条件为false时会生成空的占位注释节点那么在考虑首页白屏时间时选用v-if比较合适。条件从false变化为true的话会从空的注释节点变成真实节点条件再变为false时真实节点又会变成注释节点如果切换次数比较多那么开销会比较大频繁切换场景不建议使用v-if。 2v-show条件为false时会生成真实的节点只是为当前节点增加了display:none来控制其隐藏相比v-if生成空的注释节点其首次渲染开销是比较大的所以不建议用在考虑首屏白屏时间的场景。如果我们频繁切换v-show的值从display:none到display:block之间的切换比起空的注释节点和真实节点的开销要小很多这种场景就建议使用v-show。 可以通过vue中v-if和v-show的区别源码分析了解v-if和v-show详细过程。 十八、v-for中key的作用 在v-for进行循环展示过程中当数据发生变化进行渲染的过程中会进行新旧节点列表的比对。首先新旧vnode列表首先通过首首、尾尾、首尾和尾首的方式进行比对如果key相同则采取原地复用的策略进行节点的移动。 如果首尾两两比对的方式找不到对应关系继续通过key和vnode的对应关系进行寻找。 如果key和vnode对应关系中找不到继续通过sameVnode的方式在未比对的节点中进行寻找。 如果都找不到则将其按照新vnode进行createElm的方式进行创建这种方式是比节点移动的方式计算量更大。 最后将旧的vnode列表中没有进行匹配的vnode中的vnode.elm在父节点中移除。 简单总结就是新的vnode列表在旧的vnode列表中去寻找具有相同的key的节点进行原地复用如果找不到则通过创建的方式createElm去创建一个如果旧的vnode列表中没有进行匹配则在父节点中移除其vnode.elm。这就是原地复用逻辑的大体实现。 具体key和diff算法的关系可以参考vue2从数据变化到视图变化diff算法图解 十九、v-for和v-if能同时使用吗 答案是用了也能出来预期的效果但是会有性能浪费。 同时包含v-for和v-if的template模板在编辑阶段会执行v-for比v-if优先级更高的编译流程在生成vnode的阶段会包含属性isComment为true的空白占位vnode在patch阶段会生成真实的占位节点。虽然一个空的占位节点无妨但是如果数据量比较大的话也是一个性能问题。 当然可以在获取到数据(一般是在beforeCreate或者created阶段)时进行过滤处理也可以通过计算属性对其进行处理。 可以通过v-for和v-if可以一起使用吗了解v-for和v-if的详细过程。 二十、vue中的data为什么是函数 答案是是不是一定是函数得看场景。并且也无需担心什么时候该将data写为函数还是对象因为vue内部已经做了处理并在控制台输出错误信息。 场景一new Vue({data: ...}) 这种场景主要为项目入口或者多个html页面各实例化一个Vue时这里的data即可用对象的形式也可用工厂函数返回对象的形式。因为这里的data只会出现一次不存在重复引用而引起的数据污染问题。 场景二组件场景中的选项 在生成组件vnode的过程中组件会在生成构造函数的过程中执行合并策略 // data合并策略 strats.data function (parentVal,childVal,vm ) {if (!vm) {if (childVal typeof childVal ! function) {process.env.NODE_ENV ! production warn(The data option should be a function that returns a per-instance value in component definitions.,vm);return parentVal}return mergeDataOrFn(parentVal, childVal)}return mergeDataOrFn(parentVal, childVal, vm) };如果合并过程中发现子组件的数据不是函数即typeof childVal ! function成立进而在开发环境会在控制台输出警告并且直接返回parentVal说明这里压根就没有把childVal中的任何data信息合并到options中去。 可以通过vue中的data为什么是函数了解详细过程。 二十一、this.$watch 使用场景用来监听数据的变化当数据发生变化的时候可以做一些业务逻辑的处理。 配置参数 deep监听数据的深层变化immediate立即触发回调函数 实现思路 Vue构造函数定义完成以后在执行stateMixin(Vue)时为Vue.prototype上定义$watch。该方法通过const watcher new Watcher(vm, expOrFn, cb, options)进行Watcher的实例化将options中的user属性设置为true。并且$watch逻辑结束的会返回函数function unwatchFn () { watcher.teardown() }用来取消侦听的函数。 可以通过watch选项和$watch方法的区别vue中的watch和$watch监听的事件执行几次来了解详细过程。 二十二、计算属性和侦听属性的区别 相同点 两者都是Watcher实例化过程中的产物 计算属性 使用场景模板内的表达式主要用于简单运算对于复杂的计算逻辑可以用计算属性计算属性是基于它们的响应式依赖进行缓存的当依赖的数据未发生变化时多次调用无需重复执行函数计算属性计算结果依赖于data中的值同步操作不支持异步 侦听属性 使用场景当需要在数据变化时执行异步或开销较大的操作时可以用侦听属性可配置参数可以通过配置immediate和deep来控制立即执行和深度监听的行为侦听属性侦听的是data中定义的 计算属性请参考vue2从数据变化到视图变化计算属性 侦听属性请参考vue2从数据变化到视图变化侦听器 二十三、v-model // main.js new Vue({el: #app,data() {return {msg: };},template: divinput v-modelmsg placeholderedit mepmsg is: {{ msg }}/p/div });普通inputinput中的v-model最终通过target.addEventListener处理成在节点上监听input事件function($event){msg$event.target.value}}的形式当input值变化时msg也跟着改变。 // main.js const inputBox {template: input input$emit(input, $event.target.value), };new Vue({el: #app,template: divinput-box v-modelmsg/input-boxp{{msg}}/p/div,components: {inputBox},data() {return {msg: hello world!};}, });组件v-model在组件中则通过给点击事件绑定原生事件当触发到$emit的时候再进行回调函数ƒunction input($$v) {msg$$v}的执行进而达到子组件修改父组件中数据msg的目的。 二十四、v-slot v-slot产生的主要目的是在组件的使用过程中可以让父组件有修改子组件内容的能力就像在子组件里面放了个插槽让父组件往插槽内塞入父组件中的楔子并且父组件在子组件中嵌入的楔子也可以访问子组件中的数据。v-slot的产生让组件的应用更加灵活。 1、具名插槽 let baseLayout {template: div classcontainerheaderslot nameheader/slot/headermainslot/slot/mainfooterslot namefooter/slot/footer/div,data() {return {url: };} };new Vue({el: #app,template: base-layouttemplate v-slot:headerh1title-txt/h1/templatepparagraph-1-txt/ppparagraph-2-txt/ptemplate v-slot:footerpfoot-txt/p/template/base-layout,components: {baseLayout} });引入的组件baseLayout中的template被添加了属性v-slot:header和v-slot:footer子组件中定义了对应的插槽被添加了属性nameheader和namefooter未被进行插槽标识的内容被插入到了匿名的slot/slot中。 2、作用域插槽 let currentUser {template: spanslot nameuser v-bind:userDatachildData{{childData.firstName}}/slot/span,data() {return {childData: {firstName: first,lastName: last}};} };new Vue({el: #app,template: current-usertemplate v-slot:userslotProps{{slotProps.userData.lastName}}/template/current-user,components: {currentUser} });当前例子中作用域插槽通过v-bind:userDatachildData的方式将childData作为参数父组件中通过v-slot:userslotProps的方式进行接收为父组件使用子组件中的数据提供了可能。 v-slot的底层实现请参考vue中的v-slot源码分析 二十五、Vue.filters filters类似于管道流可以将上一个过滤函数的结果作为下一个过滤函数的第一个参数又可以在其中传递参数让过滤器更灵活。 // main.js文件 import Vue from vue;Vue.filter(filterEmpty, function(val) {return val || ; });Vue.filter(filterA, function(val) {return val 平时周末的; });Vue.filter(filterB, function(val, info, fn) {return val info fn; });new Vue({el: #app,template: div{{msg | filterEmpty | filterA | filterB(爱好是, transformHobby(chess))}}/div,data() {return {msg: 张三};},methods: {transformHobby(type) {const map {bike: 骑行,chess: 象棋,game: 游戏,swimming: 游泳};return map[type] || 未知;}} });其中我们对msg通过filterEmpty、filterA和filterB(爱好是, transformHobby(chess))}进行三层过滤。 Vue.filters的底层实现请查看vue中的filters源码分析 二十六、Vue.use 作用Vue.use被用来安装Vue.js插件例如vue-router、vuex、element-ui。install方法如果插件是一个对象必须提供 install 方法。如果插件是一个函数它会被作为install方法。install方法调用时会将Vue作为参数传入。调用时机该方法需要在调用 new Vue() 之前被调用。特点当 install 方法被同一个插件多次调用插件将只会被安装一次。 二十七、Vue.extend和选项extends 1、Vue.extend Vue.extend使用基础Vue构造器创建一个“子类”参数是一个包含组件选项的对象实例化的过程中可以修改其中的选项为实现功能的继承提供了思路。 new Vue({el: #app,template: divdiv idperson1/divdiv idperson2/div/div,mounted() {// 定义子类构造函数var Profile Vue.extend({template: p clickshowInfo{{name}} 喜欢 {{fruit}}/p,data: function () {return {name: 张三,fruit: 苹果}},methods: {showInfo() {console.log(${this.name}喜欢${this.fruit})}}})// 实例化1挂载到#person1上new Profile().$mount(#person1)// 实例化2并修改其data选项挂载到#person2上new Profile({data: function () {return {name: 李四,fruit: 香蕉}},}).$mount(#person2)}, });在当前例子中通过Vue.extend构建了子类构造函数Profile可以通过new Profile的方式实例化无数个vm实例。我们定义初始的template、data和methods供vm进行使用如果有变化在实例的过程中传入新的选项参数即可比如例子中实例化第二个vm的时候就对data进行了调整。 2、选项extends extends允许声明扩展另一个组件 (可以是一个简单的选项对象或构造函数)而无需使用 Vue.extend。这主要是为了便于扩展单文件组件以实现组件继承的目的。 const common {template: div{{name}}/div,data() {return {name: 表单}} }const create {extends: common,data() {return {name: 新增表单}} }const edit {extends: common,data() {return {name: 编辑表单}} }new Vue({el: #app,template: divcreate/createedit/edit/div,components: {create,edit,} });当前极简demo中定义了公共的表单common然后又在新增表单组件create和编辑表单组件edit中扩展了common。 二十八、Vue.mixin和选项mixins 全局混入和局部混入视情况而定主要区别在全局混入是通过Vue.mixin的方式将选项混入到了Vue.options中在所有获取子组件构建函数的时候都将其进行了合并是一种影响全部组件的混入策略。 而局部混入是将选项通过配置mixins选项的方式合并到当前的子组件中只有配置了mixins选项的组件才会受到混入影响是一种局部的混入策略。 二十九、Vue.directive和directives 1、使用场景 主要用于对于DOM的操作比如文本框聚焦节点位置控制、防抖节流、权限管理、复制操作等功能 2、钩子函数 bind只调用一次指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。inserted被绑定元素插入父节点时调用 (仅保证父节点存在但不一定已被插入文档中)。update所在组件的 VNode 更新时调用但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新。componentUpdated指令所在组件的 VNode 及其子 VNode 全部更新后调用。unbind只调用一次指令与元素解绑时调用。 3、钩子函数参数 el指令所绑定的元素可以用来直接操作 DOM。binding一个对象包含以下 property name指令名不包括 v- 前缀。value指令的绑定值例如v-my-directive1 1 中绑定值为 2。oldValue指令绑定的前一个值仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。expression字符串形式的指令表达式。例如 v-my-directive1 1 中表达式为 1 1。arg传给指令的参数可选。例如 v-my-directive:foo 中参数为 foo。modifiers一个包含修饰符的对象。例如v-my-directive.foo.bar 中修饰符对象为 { foo: true, bar: true }。vnodeVue 编译生成的虚拟节点。oldVnode上一个虚拟节点仅在 update 和 componentUpdated 钩子中可用。 4、动态指令参数 指令的参数可以是动态的。例如在 v-mydirective:[argument]value 中argument 参数可以根据组件实例数据进行更新这使得自定义指令可以在应用中被灵活使用。 三十、vue中的原生事件 vue中可以通过或者v-on的方式绑定事件也可为其添加修饰符。 new Vue({el: #app,template: div clickdivClicka clicktaClick href点击/a/div,methods: {divClick() {console.log(divClick)},aClick() {console.log(aClick)},} })以上例子如果点击a会触发其默认行为如果href不为空还会进行跳转。除此之外点击还会继续触发div上绑定的点击事件。 如果通过click.stop.preventaClick的方式为a标签的点击事件添加修饰符stop和prevent那么就不会触发其a的默认行为即使href不为空也不会进行跳转同时div上的点击事件也不会进行触发。 模板的渲染一般分为编译生成render函数、render函数执行生成vNode和patch进行渲染。下面按照这步骤进行简单分析。 1、render 通过编译生成的render函数 with(this) {return _c(div, {on: {click: divClick}}, [_c(a, {attrs: {href: http://www.baidu.com},on: {click: function ($event) {$event.stopPropagation();$event.preventDefault();return aClick($event)}}}, [_v(点击)])]) }其中div的on作为div事件描述。a标签的attrs作为属性描述on作为事件描述在描述中.stop被编译成了$event.stopPropagation()来阻止事件冒泡.prevent被编译成了$event.preventDefault()用来阻止a标签的默认行为。 2、vNode 通过执行Vue.prototype._render将render函数转换成vNode。 3、patch patch的过程中当完成$el节点的渲染后会执行invokeCreateHooks(vnode, insertedVnodeQueue)逻辑其中针对attrs会将其设置为$el的真实属性当前例子中会为a标签设置herf属性。针对on会通过target.addEventListener的方式将其处理过的事件绑定到$el上当前例子中会分别对div和a中的click进行处理再通过addEventListener的方式进行绑定。 小结 vue中的事件从编译生成render再通过Vue.prototype._render函数执行render到生成vNode主要是通过on作为描述。在patch渲染阶段将on描述的事件进行处理再通过addEventListener的方式绑定到$el上。 三十一、常用修饰符 1、表单修饰符 1.lazy 在默认情况下v-model 在每次 input 事件触发后将输入框的值与数据进行同步 可以添加 lazy 修饰符从而转为在 change 事件之后进行同步: input v-model.lazymsg2.number 如果想自动将用户的输入值转为数值类型可以给 v-model 添加 number 修饰符 input v-model.numberage typenumber3.trim 如果要自动过滤用户输入的首尾空白字符可以给 v-model 添加 trim 修饰符 input v-model.trimmsg2、事件修饰符 1.stop 阻止单击事件继续传播。 !--这里只会触发a-- div clickdivClicka v-on:click.stopaClick点击/a/div2.prevent 阻止标签的默认行为。 a hrefhttp://www.baidu.com v-on:click.preventaClick点击/a3.capture 事件先在有.capture修饰符的节点上触发然后在其包裹的内部节点中触发。 !--这里先执行divClick事件然后再执行aClick事件-- div clickdivClicka v-on:clickaClick点击/a/div4.self 只当在 event.target 是当前元素自身时触发处理函数即事件不是从内部元素触发的。 !--在a标签上点击时只会触发aClick事件只有点击phrase的时候才会触发divClick事件-- div click.selfdivClickphrasea v-on:clickaClick点击/a/div5.once 不像其它只能对原生的 DOM 事件起作用的修饰符.once 修饰符还能被用到自定义的组件事件上表示当前事件只触发一次。 a v-on:click.onceaClick点击/a6.passive .passive 修饰符尤其能够提升移动端的性能 !-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 -- !-- 而不会等待 onScroll 完成 -- !-- 这其中包含 event.preventDefault() 的情况 -- div v-on:scroll.passiveonScroll.../div3、其他修饰符 除了表单和事件的修饰符Vue还提供了很多其他修饰符在使用的时候可以查阅文档。 小结 Vue中提供了很多好用的功能和api那么修饰符的出现就为功能和api提供了更为丰富的扩展属性和更大的灵活度。 三十二、vue-router vue路由是单页面中视图切换的方案有三种mode: hash#后的仅仅作为参数不属于url部分history路径作为请求url请求资源链接如果找不到会出现404错误abstract服务端渲染场景 hash场景下会出现url链接再修改其view-router中对应的值。 了解vue-router的底层实现请参考vue2视图切换vue-router 三十三、vuex vuex是状态管理仓库一般使用的场景为多个视图依赖于同一状态来自不同视图的行为需要变更同一状态。其管理的状态是响应式的修改也只能显式提交mutation的方式修改。vuex有state、getter、mutation、action和module五个核心并且通过module实现了vuex树的管理。 了解vuex的底层实现请参考vue2状态管理vuex 三十四、eventBus 使用场景兄弟组件传参 const eventBus new Vue();const A {template: div clicksendcomponent-a/div,methods: {send() {eventBus.$emit(sendData, data from A)}}, }const B {template: divcomponent-b/div,created() {eventBus.$on(sendData, (args) {console.log(args)})}, }new Vue({el: #app,components: {A,B,},template: divA/AB/B/div, })在当前例子中A组件和B组件称为兄弟组件A组件通过事件总线eventBus中的$emit分发事件B组件则通过$on来监听事件。 实现原理eventsMixin export function eventsMixin (Vue: ClassComponent) {const hookRE /^hook:/Vue.prototype.$on function (event: string | Arraystring, fn: Function): Component {const vm: Component thisif (Array.isArray(event)) {for (let i 0, l event.length; i l; i) {vm.$on(event[i], fn)}} else {(vm._events[event] || (vm._events[event] [])).push(fn)// optimize hook:event cost by using a boolean flag marked at registration// instead of a hash lookupif (hookRE.test(event)) {vm._hasHookEvent true}}return vm}Vue.prototype.$once function (event: string, fn: Function): Component {const vm: Component thisfunction on () {vm.$off(event, on)fn.apply(vm, arguments)}on.fn fnvm.$on(event, on)return vm}Vue.prototype.$off function (event?: string | Arraystring, fn?: Function): Component {const vm: Component this// allif (!arguments.length) {vm._events Object.create(null)return vm}// array of eventsif (Array.isArray(event)) {for (let i 0, l event.length; i l; i) {vm.$off(event[i], fn)}return vm}// specific eventconst cbs vm._events[event]if (!cbs) {return vm}if (!fn) {vm._events[event] nullreturn vm}// specific handlerlet cblet i cbs.lengthwhile (i--) {cb cbs[i]if (cb fn || cb.fn fn) {cbs.splice(i, 1)break}}return vm}Vue.prototype.$emit function (event: string): Component {const vm: Component thisif (process.env.NODE_ENV ! production) {const lowerCaseEvent event.toLowerCase()if (lowerCaseEvent ! event vm._events[lowerCaseEvent]) {tip(Event ${lowerCaseEvent} is emitted in component ${formatComponentName(vm)} but the handler is registered for ${event}. Note that HTML attributes are case-insensitive and you cannot use v-on to listen to camelCase events when using in-DOM templates. You should probably use ${hyphenate(event)} instead of ${event}.)}}let cbs vm._events[event]if (cbs) {cbs cbs.length 1 ? toArray(cbs) : cbsconst args toArray(arguments, 1)const info event handler for ${event}for (let i 0, l cbs.length; i l; i) {invokeWithErrorHandling(cbs[i], vm, args, vm, info)}}return vm} }在Vue构造函数定义完执行的eventsMixin函数中在Vue.prototype上分别定义了$on、$emit、$off和$once的方法易实现对事件的绑定、分发、取消和只执行一次的方法。eventBus就是利用了当new Vue实例化后实例上的$on、$emit、$off和$once进行数据传递。 三十五、ref 使用场景 父组件获取子组件数据或者执行子组件方法 const A {template: div{{childData.age}}/div,data() {return {childData: {name: qb,age: 30},}},methods: {increaseAge() {this.childData.age;}} }new Vue({el: #app,components: {A,},template: A refchildRef click.nativechangeChildData/A,methods: {changeChildData() {// 执行子组件的方法this.$refs.childRef.increaseAge()// 获取子组件的数据console.log(this.$refs.childRef.childData);},} })在当前例子中通过refchildRef的方式在当前组件中定义一个ref可以通过this.$refs.childRef的方式获取到子组件A。可以通过this.$refs.childRef.increaseAge()的方式执行子组件中age增加的方法也可以通过this.$refs.childRef.childData的方式获取到子组件中的数据。 三十六、props 使用场景 父子传参 const A {template: div clickemitData{{childData}}/div,props: [childData],methods: {emitData() {this.$emit(emitChildData, data from child)}}, }new Vue({el: #app,components: {A},template: A :childDataparentData emitChildDatagetChildData/A,data() {return {parentData: data from parent}},methods: {getChildData(v) {console.log(v);}} })从当前例子中可以看出数据父传子是通过:childDataparentData的方式数据子传父是通过this.$emit(emitChildData, data from child)的方式然后父组件通过emitChildDatagetChildData的方式进行获取。 1、父组件render函数 new Vue中传入的模板template经过遍历生成的render函数如下 with(this) {return _c(A, {attrs: {childData: parentData},on: {emitChildData: getChildData}}) }其中data部分有attrs和on来描述属性和方法。 在通过createComponent创建组件vnode的过程中会通过const propsData extractPropsFromVNodeData(data, Ctor, tag)的方式获取props通过const listeners data.on的方式获取listeners最后将其作为参数通过new VNode(options)的方式实例化组件vnode。 2、子组件渲染 在通过const child vnode.componentInstance createComponentInstanceForVnode( vnode, activeInstance )创建组件实例的过程中会执行到组件继承自Vue的._init方法通过initEvents将事件处理后存储到vm._events中通过initProps将childData赋值到子组件A的vm实例上并进行响应式处理让其可以通过vm.childData的方式访问并且数据发生变化时视图也可以发生改变。 组件模板编译后对应的render函数是: with(this) {return _c(div, {on: {click: emitData}}, [_v(_s(childData))]) }在createElm完成节点的创建后在invokeCreateHooks(vnode, insertedVnodeQueue)阶段给DOM原生节点节点绑定emitData。 3、this.$emit 在点击执行this.$emit时会通过var cbs vm._events[event]取出_events中的事件进行执行。 至此父组件中的传递的数据就在子组件中可以通过this.xxx的方式获得也可以通过this.$emit的方式将子组件中的数据传递给父组件。 prop数据发生改变引起视图变化的底层逻辑请参考vue2从数据变化到视图变化props引起视图变化详解 三十七、$attrs和$listeners 使用场景 父子组件非props属性和非native方法传递 // main.js文件 import Vue from vue;const B {template: div clickemitData{{ formParentData }}/div,data() {return {formParentData: }},inheritAttrs: false,created() {this.formParentData this.$attrs;console.log(this.$attrs, --------------a-component-$attrs)console.log(this.$listeners, --------------b-component-$listeners)},methods: {emitData() {this.$emit(onFun, form B component)}}, }const A {template: B v-bind$attrs v-on$listeners/B,components: {B,},props: [propData],inheritAttrs: false,created() {console.log(this.$attrs, --------------b-component-$attrs)console.log(this.$listeners, --------------b-component-$listeners)} }new Vue({el: #app,components: {A,},template: A :attrDataparentData :propDataparentData click.nativenativeFun onFunonFun/A,data() {return {parentData: msg}},methods: {nativeFun() {console.log(方法A);},onFun(v) {console.log(方法B, v);},} })当前例子中new Vue的template模板中有attrData、propData、click.native和onFun在进行传递。实际运行后在A组件中this.$attrs为{attrData: msg}this.$listeners为{onFun:f(...)}。在A组件中通过v-bind$attrs和v-on$listeners的方式继续进行属性和方法的传递在B组件中就可以获取到A组件中传入的$attrs和$listeners。 当前例子中完成了非props属性和非native方法的传递并且通过v-bind$attrs和v-on$listeners的方式实现了属性和方法的跨层级传递。 同时通过this.$emit的方法触发了根节点中onFun事件。 关于例子中的inheritAttrs: false默认情况下父作用域的不被认作props的attribute绑定将会“回退”且作为普通的HTML属性应用在子组件的根元素上。当撰写包裹一个目标元素或另一个组件的组件时这可能不会总是符合预期行为。通过设置inheritAttrs到false这些默认行为将会被去掉。 三十八、$parent和$children 使用场景 利用父子关系进行数据的获取或者方法的调用 const A {template: div clickchangeParentData{{childRandom}}/div,data() {return {childRandom: Math.random()}},mounted() {console.log(this.$parent.parentCount, --child-created--); // 获取父组件中的parentCount},methods: {changeParentData() {console.log(this.$parent); // 打印当前实例的$parentthis.$parent.changeParentData(); // 调用当前父级中的方法changeParentData},changeChildData() {this.childRandom Math.random();}} } const B {template: divb-component/div, }new Vue({el: #app,components: {A,B,},template: divA/AB/Bp{{parentCount}}/pbutton clickchangeChildrenData修改子组件数据/button/div,data() {return {parentCount: 1}},mounted() {console.log(this.$children[0].childRandom, --parent-created--); // 获取第一个子组件中的childRandom},methods: {changeParentData() {this.parentCount;},changeChildrenData() {console.log(this.$children); // 此时有两个子组件this.$children[0].changeChildData(); // 调起第一个子组件中的changeChildData方法}} })在当前例子中父组件可以通过this.$children获取所有的子组件这里有A组件和B组件可以通过this.$children[0].childRandom的方式获取子组件A中的数据也可以通过this.$children[0].changeChildData()的方式调起子组件A中的方法。 子组件可以通过this.$parent的方式获取父组件可以通过this.$parent.parentCount获取父组件中的数据也可以通过this.$parent.changeParentData()的方式修改父组件中的数据。 Vue中$parent和$children父子关系的底层构建请参考杂谈/parent/children的底层逻辑 三十九、inject和provide 使用场景嵌套组件多层级传参 const B {template: div{{parentData1}}{{parentData2}}/div,inject: [parentData1, parentData2], }const A {template: B/B,components: {B,}, }new Vue({el: #app,components: {A,},template: A/A,provide: {parentData1: {name: name-2,age: 30},parentData2: {name: name-2,age: 29},} })例子中在new Vue的时候通过provide提供了两个数据来源parentData1和parentData2然后跨了一个A组件在B组件中通过inject注入了这两个数据。 1、initProvide 在执行组件内部的this._init初始化方法时会执行到initProvide逻辑 export function initProvide (vm: Component) {const provide vm.$options.provideif (provide) {vm._provided typeof provide function? provide.call(vm): provide} }如果在当前vm.$options中存在provide会将其执行结果赋值给vm._provided。 2、initInjections function initInjections (vm: Component) {const result resolveInject(vm.$options.inject, vm)if (result) {toggleObserving(false)Object.keys(result).forEach(key {/* istanbul ignore else */if (process.env.NODE_ENV ! production) {defineReactive(vm, key, result[key], () {warn(Avoid mutating an injected value directly since the changes will be overwritten whenever the provided component re-renders. injection being mutated: ${key},vm)})} else {defineReactive(vm, key, result[key])}})toggleObserving(true)} } function resolveInject (inject: any, vm: Component): ?Object {if (inject) {// inject is :any because flow is not smart enough to figure out cachedconst result Object.create(null)const keys hasSymbol? Reflect.ownKeys(inject): Object.keys(inject)for (let i 0; i keys.length; i) {const key keys[i]// #6574 in case the inject object is observed...if (key __ob__) continueconst provideKey inject[key].fromlet source vmwhile (source) {if (source._provided hasOwn(source._provided, provideKey)) {result[key] source._provided[provideKey]break}source source.$parent}if (!source) {if (default in inject[key]) {const provideDefault inject[key].defaultresult[key] typeof provideDefault function? provideDefault.call(vm): provideDefault} else if (process.env.NODE_ENV ! production) {warn(Injection ${key} not found, vm)}}}return result} }如果当前组件中有选项inject会以while循环的方式不断在source source.$parent中寻找_provided然后获取到祖先组件中提供的数据源这是实现祖先组件向所有子孙后代注入依赖的核心。 四十、Vue项目能做的性能优化 1、v-if和v-show 频繁切换时使用v-show利用其缓存特性首屏渲染时使用v-if如果为false则不进行渲染 2、v-for的key 列表变化时循环时使用唯一不变的key借助其本地复用策略列表只进行一次渲染时key可以采用循环的index 3、侦听器和计算属性 侦听器watch用于数据变化时引起其他行为多使用compouter计算属性顾名思义就是新计算而来的属性如果依赖的数据未发生变化不会触发重新计算 4、合理使用生命周期 在destroyed阶段进行绑定事件或者定时器的销毁使用动态组件的时候通过keep-alive包裹进行缓存处理相关的操作可以在actived阶段激活 5、数据响应式处理 不需要响应式处理的数据可以通过Object.freeze处理或者直接通过this.xxx xxx的方式进行定义需要响应式处理的属性可以通过this.$set的方式处理而不是JSON.parse(JSON.stringify(XXX))的方式 6、路由加载方式 页面组件可以采用异步加载的方式 7、插件引入 第三方插件可以采用按需加载的方式比如element-ui。 8、减少代码量 采用mixin的方式抽离公共方法抽离公共组件定义公共方法至公共js中抽离公共css 9、编译方式 如果线上需要template的编译可以采用完成版vue.esm.js如果线上无需template的编译可采用运行时版本vue.runtime.esm.js相比完整版体积要小大约30% 10、渲染方式 服务端渲染如果是需要SEO的网站可以采用服务端渲染的方式前端渲染一些企业内部使用的后端管理系统可以采用前端渲染的方式 11、字体图标的使用 有些图片图标尽可能使用字体图标 四十一、Vue项目白屏问题 1、开启gzip压缩减小文件体积。2、webpack设置productionSourceMap:false不在线上环境打包.map文件。3、路由懒加载4、异步组件的使用5、静态资源使用cdn链接引入6、采用ssr服务端渲染方案7、骨架屏或者loading效果填充空白间隙8、首次不渲染的隐藏采用v-if9、注重代码规范抽取公共组件公共js公共css样式减小代码体积。删除无用代码减少非必要注释。防止写出死循环等等10、删除辅助开发的console.log11、非Vue角度思考非重要文件采用异步加载方式、css样式采用媒体查询、采用域名分片技术、http1升级成http2、如果是SSR项目考虑服务端渲染有没有可优化的点、请求头是否带了多余信息等思路 内容有些多大体可以归类为从服务端拿到资源的速度、资源的体积和渲染是否阻塞的角度去作答。 四十二、从0到1构建一个Vue项目需要注意什么 架子选用合适的初始化脚手架(vue-cli2.0或者vue-cli3.0)请求数据axios请求的配置登录登录注册系统路由路由管理页面数据vuex全局数据管理权限权限管理系统埋点埋点系统插件第三方插件的选取以及引入方式错误错误页面入口前端资源直接当静态资源或者服务端模板拉取SEO如果考虑SEO建议采用SSR方案组件基础组件/业务组件样式样式预处理起公共样式抽取方法公共方法抽离 四十三、SSR 1、什么是服务端渲染SSR Vue.js 是构建客户端应用程序的框架。默认情况下可以在浏览器中输出 Vue 组件进行生成 DOM 和操作 DOM。然而也可以将同一个组件渲染为服务器端的 HTML 字符串将它们直接发送到浏览器最后将这些静态标记激活为客户端上完全可交互的应用程序。 2、为什么使用服务端渲染SSR 与传统 SPA (单页应用程序 (Single-Page Application)) 相比服务器端渲染 (SSR) 的优势主要在于 更好的 SEO由于搜索引擎爬虫抓取工具可以直接查看完全渲染的页面。更快的内容到达时间 (time-to-content)特别是对于缓慢的网络情况或运行缓慢的设备。 3、使用服务器端渲染 (SSR) 时需要考虑的问题 使用服务器端渲染 (SSR) 时还需要有一些权衡之处 开发条件所限。浏览器特定的代码只能在某些生命周期钩子函数 (lifecycle hook) 中使用一些外部扩展库 (external library) 可能需要特殊处理才能在服务器渲染应用程序中运行。涉及构建设置和部署的更多要求。与可以部署在任何静态文件服务器上的完全静态单页面应用程序 (SPA) 不同服务器渲染应用程序需要处于 Node.js server 运行环境。更多的服务器端负载。在 Node.js 中渲染完整的应用程序显然会比仅仅提供静态文件的 server 更加大量占用 CPU 资源 (CPU-intensive - CPU 密集)因此如果你预料在高流量环境 (high traffic) 下使用请准备相应的服务器负载并明智地采用缓存策略。 四十四、scoped 在Vue项目开发的项目中如果样式中未使用scoped组件间的样式会出现覆盖的问题。 反例 // app.vue文件 templatedivh3 classtitleapp-txt/h3child/child/div /templatescript import child from /components/child; export default {components: { child }, }; /scriptstyle .title {color: red; } /style// child.vue文件 templateh3 classtitlechild-txt/h3 /templatestyle.title {color: green;} /style父组件和子组件的样式颜色都为green子组件中的样式覆盖了父组件的样式。 正例 templateh3 classtitlechild-txt/h3 /templatestyle scoped.title {color: green;} /style此时父组件中颜色为red子组件中颜色为green。 主要原因 例子中的DOM节点和CSS层叠样式中都被添加了data-v-xxx来表示唯一所以scoped是给当前组件的节点和样式唯一标识为data-v-xxx避免了样式覆盖。 给大家推荐一个实用面试题库 1、前端面试题库 面试必备            推荐★★★★★ 地址web前端面试题库
http://www.w-s-a.com/news/165727/

相关文章:

  • 可不可以异地建设网站学做网站培训班要多少钱
  • 茌平网站建设公司免费的云服务器有哪些
  • 手机网站单页面铜陵网站制作公司
  • 网站logo怎么做才清晰千库网官网首页登录
  • 山西省建设银行网站首页长沙网站建设制作
  • 襄阳市做网站 优帮云百度搜索次数统计
  • 自己做视频直播网站盐城做网站多少钱
  • 买个网站服务器多少钱重庆做的好的房产网站
  • 深圳定制建站网站建设推广关键词怎么设置
  • 宝山网站建设 网站外包修改wordpress版权
  • 建立网站的基本步骤新网站多久会被百度收录
  • 软件设计开发流程图廊坊关键词seo排名方案
  • 南山住房和建设局网站网站被k 多久恢复
  • 阿里买域名 电脑做网站做简历哪个网站好
  • 个人网站免费服务器单页网站的域名
  • 网站设计简单讲解小店怎么做网站
  • 校园网站的意义wordpress去除更新
  • 网站开发用python吗常用的网页开发工具有哪些
  • 北京市住房建设投资建设网站做商城网站要哪些流程
  • seo网站改版杭州建设局官网
  • 物流网站建设策划书泰然建设网站
  • 百度做网站的费用采集发布wordpress
  • 网站运维公司有哪些防录屏网站怎么做
  • 昆明做网站seo的网站制作专业
  • 聊城制作手机网站公司wordpress 头条
  • 商城微网站模板一般电商网站做集群
  • winserver2008上用iis发布网站嵊州网站制作
  • 网站内页权重怎么查辽宁建设工程信息网怎么上传业绩
  • 丰都网站建设价格镇江网站制作费用
  • app手机网站建设黄网站建设定制开发服务