Vue.js总结

Vue.js
Vue.js 路由的钩⼦函数?

答:首页可以控制导航跳转, beforeEach , afterEach 等,⼀般⽤于页⾯ title
的修改。⼀些需要登录才能调整⻚⾯的重定向功能。 beforeEach 主要有3个参数 to , from , next 。 to :
route 即将进⼊的⽬标路由对象。 from : route 当前导航正要离开的路由。 next : function ⼀定要调⽤该⽅法
resolve 这个钩⼦。执⾏效果依赖next ⽅法的调⽤参数。可以控制网页的跳转

vuex是什么?怎么使⽤?哪种功能场景使⽤它?

答: 只⽤来读取的状态集中放在 store 中; 改变状态的⽅式是提交 mutations ,这是个同步的事物; 异步逻辑应该封装在
action 中。 在 main.js 引⼊ store ,注⼊。新建了⼀个⽬录 store , … export
场景有:单⻚应⽤中,组件之间的状态、⾳乐播放、登录状态、加⼊购物⻋ state : Vuex 使⽤单⼀状态树,即每个应⽤将仅仅包含⼀个
store 实例,但单⼀状态 树和模块化并不冲突。存放的数据状态,不可以直接修改⾥⾯的数据。 mutations : mutations
定义的⽅法动态修改 Vuex 的 store 中的状态或数据 getters :类似 vue 的计算属性,主要⽤来过滤⼀些数据。
action : actions 可以理解为通过将 mutations
⾥⾯处⾥数据的⽅法变成可异步的处理数据的⽅法,简单的说就是异步操作数据。 view 层通过 store.dispath 来分发 action
modules :项⽬特别复杂的时候,可以让每⼀个模块拥有⾃⼰的 state 、mutation 、 action 、 getters
,使得结构⾮常清晰,⽅便管理

Vue.js 如何让CSS只在当前组件中起作⽤?

答:将当前组件的

Vue.js 指令v-el的作⽤是什么?

答:提供⼀个在页⾯上已存在的 DOM 元素作为 Vue 实例的挂载⽬标.可以是 CSS 选择器,也可以是⼀个 HTMLElement 实例,

在 Vue.js 中使⽤插件的步骤?

答: 采⽤ ES6 的 import … from … 语法或 CommonJS 的 require() ⽅法引⼊插件 使⽤全局⽅法
Vue.use( plugin ) 使⽤插件,可以传⼊⼀个选项对象 Vue.use(MyPlugin, { someOption: true
})

Vue.js 路由之间跳转?

答:声明式(标签跳转) 编程式( js跳转)

router.push('index') Vue.js 组件中 data 什么时候可以使⽤对象? 答:组件复⽤时所有组件实例都会共享 data ,如果 data 是对象的话,就会造成⼀个组件 修改 data 以后会影响到其他所有组件,所以需要将 data

写成函数,每次⽤到就调⽤ ⼀次函数获得新的数据。当我们使⽤ new Vue() 的⽅式的时候,⽆论我们将 data 设置为对象还是函数都是可
以的,因为 new Vue() 的⽅式是⽣成⼀个根组件,该组件不会复⽤,也就不存在共享 data 的情况了 Vue-Router
中导航守卫有哪些? 答:完整的导航解析流程 导航被触发。 在失活的组件里调用离开守卫。 调用全局的 beforeEach 守卫。
在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。 在路由配置里调用 beforeEnter。 解析异步路由组件。
在被激活的组件里调用 beforeRouteEnter。 调用全局的 beforeResolve 守卫 (2.5+)。 导航被确认。
调用全局的 afterEach 钩子。 触发 DOM 更新。 用创建好的实例调用 beforeRouteEnter 守卫中传给 next
的回调函数。 Vue.js ajax 请求放在哪个生命周期中? 答:
在created的时候,视图中的dom并没有渲染出来,所以此时如果直接去操dom节点,无法找到相关的元素
在mounted中,由于此时dom已经渲染出来了,所以可以直接操作dom节点
一般情况下都放到mounted中,保证逻辑的统一性,因为生命周期是同步执行的,ajax是异步执行的

Vue.js 中相同逻辑如何抽离?

答:Vue.mixin用法 给组件每个生命周期,函数等都混入一些公共逻辑
    Vue.mixin = function (mixin: Object) {
        this.options = mergeOptions(this.options, mixin); // 将当前定义的属性合并到每个组件中
        return this
    }
    export function mergeOptions (
      parent: Object,
      child: Object,
      vm?: Component
    ): Object {
      if (!child._base) {
        if (child.extends) { // 递归合并extends
          parent = mergeOptions(parent, child.extends, vm)
        }
        if (child.mixins) { // 递归合并mixin
          for (let i = 0, l = child.mixins.length; i < l; i++) {
            parent = mergeOptions(parent, child.mixins[i], vm)
          }
        }
      }
      const options = {} // 属性及生命周期的合并
      let key
      for (key in parent) {
        mergeField(key)
      }
      for (key in child) {
        if (!hasOwn(parent, key)) {
          mergeField(key)
        }
      }
      function mergeField (key) {
        const strat = strats[key] || defaultStrat
        // 调用不同属性合并策略进行合并
        options[key] = strat(parent[key], child[key], vm, key)
      }
      return options
    }

Vue.js 中常见性能优化?

答:1.编码优化:
1.不要将所有的数据都放在data中,data中的数据都会增加getter和setter,会收集对应的watcher
2.vue 在 v-for 时给每项元素绑定事件需要用事件代理
3.SPA页面采用keep-alive缓存组件
4.拆分组件( 提高复用性、增加代码的可维护性,减少不必要的渲染 )
5.v-if 当值为false时内部指令不会执行,具有阻断功能,很多情况下使用v-if替代v-show
6.key保证唯一性 ( 默认vue会采用就地复用策略 )
7.Object.freeze 冻结数据
8.合理使用路由懒加载、异步组件
9.尽量采用runtime运行时版本
10.数据持久化的问题 (防抖、节流)
2.Vue加载性能优化: 第三方模块按需导入 (babel-plugin-component) 滚动到可视区域动态加载 ( https://tangbc.github.io/vue-virtual-scroll-list ) 图片懒加载
(https://github.com/hilongjw/vue-lazyload.git)
3.用户体验: app-skeleton骨架屏 app-shellapp壳 pwa
4.SEO优化: 预渲染插件 prerender-spa-plugin 服务端渲染ssr
5.打包优化: 使用cdn的方式加载第三方模块 多线程打包 happypack splitChunks 抽离公共文件 sourceMap生成
6.缓存,压缩 客户端缓存、服务端缓存 服务端gzip压缩

谈一下你对 Vue.js 的 MVVM 原理的理解?

答:传统MVC 传统的MVC指的是 M是指业务模型,Model(模型) V是指用户界面,View(视图)
C则是控制器,Controller(控制器) 使用MVC的目的是将M和V的实现代码分离,从而使同一个程序可以使用不同的表现形式。
用户操作会请求服务端路由,路由会调用对应的控制器来处理,控制器会获取数据。将结果返回给前端,页面重新渲染 MVVM MVVM 是
Model-View-ViewModel 的缩写 Model 代表数据模型,也可以在 Model 中定义数据修改和操作的业务逻辑。 View
代表 UI 组件,它负责将数据模型转化成 UI 展现出来。 ViewModel
监听模型数据的改变和控制视图⾏为、处理⽤户交互,简单理解就是⼀个同步View 和 Model 的对象,连接 Model 和 View
传统的前端会将数据手动渲染到页面上,MVVM模式不需要用户手动操作dom元素;
将数据绑定到viewModel层上,会自动将数据渲染到页面中,视图变化会通知viewModel层更新数据。
ViewModel就是我们MVVM模式中的桥梁. 在 MVVM 架构下, View 和 Model 之间并没有直接的联系,⽽是通过
ViewModel 进⾏交互, Model 和 ViewModel 之间的交互是双向的, 因 此 View
数据的变化会同步到Model中,⽽Model 数据的变化也会⽴即反 应到 View 上。 ViewModel 通过双向数据绑定把 View
层和 Model 层连接了起来,⽽ View 和 Model 之间的同步⼯作完全是⾃动的,⽆需⼈为⼲涉,因此开
发者只需关注业务逻辑,不需要⼿动操作DOM,不需要关注数据状态的同 步问题,复杂的数据状态维护完全由 MVVM 来统⼀管理。

请说一下 Vue.js 响应式数据的原理?

答:
1. 核心点:Object.defineProperty
2. 默认Vue在初始化数据时,会给data中的属性使用Object.defineProperty重新定义所有属性,当页面取到对应属性时。会进行依赖收集(收集当前组件的watcher) 如果属性发生变化会通知相关依赖进行更新操作。
    Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get: function reactiveGetter () {
          const value = getter ? getter.call(obj) : val
          if (Dep.target) {
            dep.depend() // ** 收集依赖 ** /
            if (childOb) {
              childOb.dep.depend()
              if (Array.isArray(value)) {
                dependArray(value)
              }
            }
          }
          return value
        },
        set: function reactiveSetter (newVal) {
          const value = getter ? getter.call(obj) : val
          if (newVal === value || (newVal !== newVal && value !== value)) {
            return
          }
          if (process.env.NODE_ENV !== 'production' && customSetter) {
            customSetter()
          }
          val = newVal
          childOb = !shallow && observe(newVal)
          dep.notify() /**通知相关依赖进行更新**/
        }
      })

Vue.js 中模板编译原理?

答:将template转化成render函数
    function baseCompile (
      template: string,
      options: CompilerOptions
    ) {
      const ast = parse(template.trim(), options) // 1.将模板转化成ast语法树
      if (options.optimize !== false) {           // 2.优化树
        optimize(ast, options)
      }
      const code = generate(ast, options)         // 3.生成树
      return {
        ast,
        render: code.render,
        staticRenderFns: code.staticRenderFns
      }
    })
    const ncname = `[a-zA-Z_][\\-\\.0-9_a-zA-Z]*`; 
    const qnameCapture = `((?:${ncname}\\:)?${ncname})`;
    const startTagOpen = new RegExp(`^<${qnameCapture}`); // 标签开头的正则 捕获的内容是标签名
    const endTag = new RegExp(`^<\\/${qnameCapture}[^>]*>`); // 匹配标签结尾的  </div>
    const attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/; // 匹配属性的
    const startTagClose = /^\s*(\/?)>/; // 匹配标签结束的  >
    let root;
    let currentParent;
    let stack = []
    function createASTElement(tagName,attrs){
        return {
            tag:tagName,
            type:1,
            children:[],
            attrs,
            parent:null
        }
    }
    function start(tagName,attrs){
        let element = createASTElement(tagName,attrs);
        if(!root){
            root = element;
        }
        currentParent = element;
        stack.push(element);
    }
    function chars(text){
        currentParent.children.push({
            type:3,
            text
        })
    }
    function end(tagName){
        const element = stack[stack.length-1];
        stack.length --; 
        currentParent = stack[stack.length-1];
        if(currentParent){
            element.parent = currentParent;
            currentParent.children.push(element)
        }
    }
    function parseHTML(html){
        while(html){
            let textEnd = html.indexOf('<');
            if(textEnd == 0){
                const startTagMatch = parseStartTag();
                if(startTagMatch){
                    start(startTagMatch.tagName,startTagMatch.attrs);
                    continue;
                }
                const endTagMatch = html.match(endTag);
                if(endTagMatch){
                    advance(endTagMatch[0].length);
                    end(endTagMatch[1])
                }
            }
            let text;
            if(textEnd >=0 ){
                text = html.substring(0,textEnd)
            }
            if(text){
                advance(text.length);
                chars(text);
            }
        }
        function advance(n) {
            html = html.substring(n);
        }
        function parseStartTag(){
            const start = html.match(startTagOpen);
            if(start){
                const match = {
                    tagName:start[1],
                    attrs:[]
                }
                advance(start[0].length);
                let attr,end
                while(!(end = html.match(startTagClose)) && (attr=html.match(attribute))){
                    advance(attr[0].length);
                    match.attrs.push({name:attr[1],value:attr[3]})
                }
                if(end){
                    advance(end[0].length);
                    return match
                }
            }
        }
    }
    // 生成语法树
    parseHTML(`<div id="container"><p>hello<span>zf</span></p></div>`);
    function gen(node){
        if(node.type == 1){
            return generate(node);
        }else{
            return `_v(${JSON.stringify(node.text)})`
        }
    }
    function genChildren(el){
        const children = el.children;
        if(el.children){
            return `[${children.map(c=>gen(c)).join(',')}]`
        }else{
            return false;
        }
    }
    function genProps(attrs){
        let str = '';
        for(let i = 0; i < attrs.length;i++){
            let attr = attrs[i];
            str+= `${attr.name}:${attr.value},`;
        }
        return `{attrs:{${str.slice(0,-1)}}}`
    }
    function generate(el){
        let children = genChildren(el);
        let code = `_c('${el.tag}'${
            el.attrs.length? `,${genProps(el.attrs)}`:''
        }${
            children? `,${children}`:''
        })`;
        return code;
    }
    // 根据语法树生成新的代码
    let code = generate(root);
    let render = `with(this){return ${code}}`;
    // 包装成函数
    let renderFn = new Function(render);
    console.log(renderFn.toString());

简述 Vue.js 中 diff 算法原理?

答:
1.先同级比较,在比较子节点
2.先判断一方有儿子一方没儿子的情况
3.比较都有儿子的情况
4.递归比较子节点
    const oldCh = oldVnode.children // 老的儿子 
    const ch = vnode.children  // 新的儿子
    if (isUndef(vnode.text)) {
        if (isDef(oldCh) && isDef(ch)) {
            // 比较孩子
            if (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue, removeOnly)
        } else if (isDef(ch)) { // 新的儿子有 老的没有
            if (isDef(oldVnode.text)) nodeOps.setTextContent(elm, '')
            addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue)
        } else if (isDef(oldCh)) { // 如果老的有新的没有 就删除
            removeVnodes(oldCh, 0, oldCh.length - 1)
        } else if (isDef(oldVnode.text)) {  // 老的有文本 新的没文本
            nodeOps.setTextContent(elm, '') // 将老的清空
        }
    } else if (oldVnode.text !== vnode.text) { // 文本不相同替换
        nodeOps.setTextContent(elm, vnode.text)
    }
    function updateChildren (parentElm, oldCh, newCh, insertedVnodeQueue, removeOnly) {
        let oldStartIdx = 0
        let newStartIdx = 0
        let oldEndIdx = oldCh.length - 1
        let oldStartVnode = oldCh[0]
        let oldEndVnode = oldCh[oldEndIdx]
        let newEndIdx = newCh.length - 1
        let newStartVnode = newCh[0]
        let newEndVnode = newCh[newEndIdx]
        let oldKeyToIdx, idxInOld, vnodeToMove, refElm
        // removeOnly is a special flag used only by <transition-group>
        // to ensure removed elements stay in correct relative positions
        // during leaving transitions
        const canMove = !removeOnly
        if (process.env.NODE_ENV !== 'production') {
          checkDuplicateKeys(newCh)
        }
        while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
          if (isUndef(oldStartVnode)) {
            oldStartVnode = oldCh[++oldStartIdx] // Vnode has been moved left
          } else if (isUndef(oldEndVnode)) {
            oldEndVnode = oldCh[--oldEndIdx]
          } else if (sameVnode(oldStartVnode, newStartVnode)) {
            patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue, newCh, newStartIdx)
            oldStartVnode = oldCh[++oldStartIdx]
            newStartVnode = newCh[++newStartIdx]
          } else if (sameVnode(oldEndVnode, newEndVnode)) {
            patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue, newCh, newEndIdx)
            oldEndVnode = oldCh[--oldEndIdx]
            newEndVnode = newCh[--newEndIdx]
          } else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right
            patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue, newCh, newEndIdx)
            canMove && nodeOps.insertBefore(parentElm, oldStartVnode.elm, nodeOps.nextSibling(oldEndVnode.elm))
            oldStartVnode = oldCh[++oldStartIdx]
            newEndVnode = newCh[--newEndIdx]
          } else if (sameVnode(oldEndVnode, newStartVnode)) { // Vnode moved left
            patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue, newCh, newStartIdx)
            canMove && nodeOps.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm)
            oldEndVnode = oldCh[--oldEndIdx]
            newStartVnode = newCh[++newStartIdx]
          } else {
            if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx)
            idxInOld = isDef(newStartVnode.key)
              ? oldKeyToIdx[newStartVnode.key]
              : findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx)
            if (isUndef(idxInOld)) { // New element
              createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx)
            } else {
              vnodeToMove = oldCh[idxInOld]
              if (sameVnode(vnodeToMove, newStartVnode)) {
                patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue, newCh, newStartIdx)
                oldCh[idxInOld] = undefined
                canMove && nodeOps.insertBefore(parentElm, vnodeToMove.elm, oldStartVnode.elm)
              } else {
                // same key but different element. treat as new element
                createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx)
              }
            }
            newStartVnode = newCh[++newStartIdx]
          }
        }
        if (oldStartIdx > oldEndIdx) {
          refElm = isUndef(newCh[newEndIdx + 1]) ? null : newCh[newEndIdx + 1].elm
          addVnodes(parentElm, refElm, newCh, newStartIdx, newEndIdx, insertedVnodeQueue)
        } else if (newStartIdx > newEndIdx) {
          removeVnodes(oldCh, oldStartIdx, oldEndIdx)
        }
      }

Vue.js 的v-model实现原理,及如何自定义v-model?

答:组件的v-model是value+input方法的语法糖
    <el-checkbox :value="" @input=""></el-checkbox>
    <el-checkbox v-model="check"></el-checkbox>
可以自己重新定义v-model的含义
    Vue.component('el-checkbox',{
        template:`<input type="checkbox" :checked="check" @change="$emit('change',$event.target.checked)">`,
        model:{
            prop:'check', // 更改默认的value的名字
            event:'change' // 更改默认的方法名
        },
        props: {
            check: Boolean
        },
    })
会将组件的v-model默认转化成value+input
    const VueTemplateCompiler = require('vue-template-compiler');
    const ele = VueTemplateCompiler.compile('<el-checkbox v-model="check"></el-checkbox>');
    // with(this) {
    //     return _c('el-checkbox', {
    //         model: {
    //             value: (check),
    //             callback: function ($$v) {
    //                 check = $$v
    //             },
    //             expression: "check"
    //         }
    //     })
    // }
    function transformModel (options, data: any) {
      const prop = (options.model && options.model.prop) || 'value'
      const event = (options.model && options.model.event) || 'input'
      ;(data.attrs || (data.attrs = {}))[prop] = data.model.value
      const on = data.on || (data.on = {})
      const existing = on[event]
      const callback = data.model.callback
      if (isDef(existing)) {
        if (
          Array.isArray(existing)
            ? existing.indexOf(callback) === -1
            : existing !== callback
        ) {
          on[event] = [callback].concat(existing)
        }
      } else {
        on[event] = callback
      }
    }
原生的 v-model,会根据标签的不同生成不同的事件和属性
    const VueTemplateCompiler = require('vue-template-compiler');
    const ele = VueTemplateCompiler.compile('<input v-model="value"/>');
    /** 
    with(this) {
    return _c('input', {
        directives: [{
            name: "model",
            rawName: "v-model",
            value: (value),
            expression: "value"
        }],
        domProps: {
            "value": (value)
        },
        on: {
            "input": function ($event) {
                if ($event.target.composing) return;
                value = $event.target.value
            }
        }
    })
    }
    */
编译时:不同的标签解析出的内容不一样
    if (el.component) {
        genComponentModel(el, value, modifiers)
        // component v-model doesn't need extra runtime
        return false
      } else if (tag === 'select') {
        genSelect(el, value, modifiers)
      } else if (tag === 'input' && type === 'checkbox') {
        genCheckboxModel(el, value, modifiers)
      } else if (tag === 'input' && type === 'radio') {
        genRadioModel(el, value, modifiers)
      } else if (tag === 'input' || tag === 'textarea') {
        genDefaultModel(el, value, modifiers)
      } else if (!config.isReservedTag(tag)) {
        genComponentModel(el, value, modifiers)
        // component v-model doesn't need extra runtime
        return false
      }
运行时:会对元素处理一些关于输入法的问题
    inserted (el, binding, vnode, oldVnode) {
        if (vnode.tag === 'select') {
          // #6903
          if (oldVnode.elm && !oldVnode.elm._vOptions) {
            mergeVNodeHook(vnode, 'postpatch', () => {
              directive.componentUpdated(el, binding, vnode)
            })
          } else {
            setSelected(el, binding, vnode.context)
          }
          el._vOptions = [].map.call(el.options, getValue)
        } else if (vnode.tag === 'textarea' || isTextInputType(el.type)) {
          el._vModifiers = binding.modifiers
          if (!binding.modifiers.lazy) {
            el.addEventListener('compositionstart', onCompositionStart)
            el.addEventListener('compositionend', onCompositionEnd)
            // Safari < 10.2 & UIWebView doesn't fire compositionend when
            // switching focus before confirming composition choice
            // this also fixes the issue where some browsers e.g. iOS Chrome
            // fires "change" instead of "input" on autocomplete.
            el.addEventListener('change', onCompositionEnd)
            /* istanbul ignore if */
            if (isIE9) {
              el.vmodel = true
            }
          }
        }
      }

Vue.js 组件的生命周期?

答:总共分为8个阶段创建前/后,载⼊前/后,更新前/后,销毁前/后 创建前/后: 在 beforeCreate 阶段, vue
实例的挂载元素 el 和数据对象 data 都为undefined ,还未初始化。在 created 阶段, vue 实例的数据对象
data 有了,el还 没有 载⼊前/后:在 beforeMount 阶段, vue 实例的 e l 和 d a t a 都 初 始 化 了 , 但 还 是 挂 载 之 前 为 虚 拟 的 d o m 节 点 , d a t a . m e s s a g e 还 未 替 换 。 在 m o u n t e d 阶 段 , v u e 实 例 挂 载 完 成 , d a t a . m e s s a g e 成 功 渲 染 。 更 新 前 / 后 : 当 d a t a 变 化 时 , 会 触 发 b e f o r e U p d a t e 和 u p d a t e d ⽅ 法 销 毁 前 / 后 : 在 执 ⾏ d e s t r o y ⽅ 法 后 , 对 d a t a 的 改 变 不 会 再 触 发 周 期 函 数 , 说 明 此 时 v u e 实 例 已 经 解 除 了 事 件 监 听 以 及 和 d o m 的 绑 定 , 但 是 d o m 结 构 依 然 存 在 要 掌 握 每 个 生 命 周 期 什 么 时 候 被 调 用 b e f o r e C r e a t e 在 实 例 初 始 化 之 后 , 数 据 观 测 ( d a t a o b s e r v e r ) 之 前 被 调 用 。 c r e a t e d 实 例 已 经 创 建 完 成 之 后 被 调 用 。 在 这 一 步 , 实 例 已 完 成 以 下 的 配 置 : 数 据 观 测 ( d a t a o b s e r v e r ) , 属 性 和 方 法 的 运 算 , w a t c h / e v e n t 事 件 回 调 。 这 里 没 有 el 和 data 都初始化了,但还是挂载之前为虚拟的 dom 节点, data.message 还未替换。在 mounted 阶段, vue 实例挂载完成, data.message 成功渲染。 更新前/后:当 data 变化时,会触发 beforeUpdate 和 updated ⽅法 销毁前/后:在执⾏ destroy ⽅法后,对 data 的改变不会再触发周期函数,说明此时 vue 实例已经解除了事件监听以及和 dom 的绑定,但是 dom 结构依然存在 要掌握每个生命周期什么时候被调用 beforeCreate 在实例初始化之后,数据观测(data observer) 之前被调用。 created 实例已经创建完成之后被调用。在这一步,实例已完成以下的配置:数据观测(data observer),属性和方法的运算, watch/event 事件回调。这里没有 eldatadomdata.messagemountedvuedata.message/databeforeUpdateupdated/destroydatavuedomdombeforeCreate(dataobserver)created(dataobserver)watch/eventel beforeMount
在挂载开始之前被调用:相关的 render 函数首次被调用。 mounted el 被新创建的 vm.$el
替换,并挂载到实例上去之后调用该钩子。 beforeUpdate 数据更新时调用,发生在虚拟 DOM 重新渲染和打补丁之前。 updated
由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。 beforeDestroy
实例销毁之前调用。在这一步,实例仍然完全可用。 destroyed Vue 实例销毁后调用。调用后,Vue
实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。 该钩子在服务器端渲染期间不被调用。
要掌握每个生命周期内部可以做什么事 created 实例已经创建完成,因为它是最早触发的原因可以进行一些数据,资源的请求。 mounted
实例已经挂载完成,可以进行一些DOM操作 beforeUpdate 可以在这个钩子中进一步地更改状态,这不会触发附加的重渲染过程。
updated 可以执行依赖于 DOM 的操作。然而在大多数情况下,你应该避免在此期间更改状态,因为这可能会导致更新无限循环。
该钩子在服务器端渲染期间不被调用。 destroyed 可以执行一些优化操作,清空定时器,解除绑定事件

什么是vue⽣命周期?

答: Vue 实例从创建到销毁的过程,就是⽣命周期。从开始创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、销毁等⼀系列过程,称之为
Vue 的⽣命周期。

vue⽣命周期的作⽤是什么?

答:它的⽣命周期中有多个事件钩⼦,让我们在控制整个Vue实例的过程时更容易形成好的 逻辑。

vue⽣命周期总共有⼏个阶段?

答:它可以总共分为 8 个阶段:创建前/后、载⼊前/后、更新前/后、销毁前/销毁后。

第⼀次页⾯加载会触发哪⼏个钩⼦?

答:会触发下⾯这⼏个 beforeCreate 、 created 、 beforeMount 、 mounted 。

DOM 渲染在哪个周期中就已经完成?

答: DOM 渲染在 mounted 中就已经完成了

描述组件渲染和更新过程?

答:渲染组件时,会通过Vue.extend方法构建子组件的构造函数,并进行实例化。最终手动调用$mount()进行挂载。更新组件时会进行patchVnode流程.核心就是diff算法

Vue.js 组件如何通信以及有哪些方式?

答: 父子间通信 父->子通过props、子-> 父 o n 、 on、 onemit 获取父子组件实例的方式 p a r e n t 、 parent、 parentchildren
在父组件中提供数据子组件进行消费 Provide、inject Ref获取实例的方式调用组件的属性或者方法 Event Bus
实现跨组件通信 Vuex状态管理实现通信

  1. ⽗⼦通信 ⽗组件通过 props 传递数据给⼦组件,⼦组件通过 emit 发送事件传递数据给⽗组件, 这两种⽅式是最常⽤的⽗⼦通信实现办法。 这种⽗⼦通信⽅式也就是典型的单向数据流,⽗组件通过 props 传递数据,⼦组件不能 直接修改
    props ,⽽是必须通过发送事件的⽅式告知⽗组件修改数据。 另外这两种⽅式还可以使⽤语法糖 v-model 来直接实现,因为
    v-model 默认会解析成 名为 value 的 prop 和名为 input 的事件。这种语法糖的⽅式是典型的双向绑定, 常⽤于 UI
    控件上,但是究其根本,还是通过事件的⽅法让⽗组件修改数据。 当然我们还可以通过访问 $parent 或者 $children
    对象来访问组件实例中的⽅法和数 据。 另外如果你使⽤ Vue 2.3 及以上版本的话还可以使⽤ $listeners 和 .sync
    这两个属 性。 $listeners 属性会将⽗组件中的 (不含 .native 修饰器的) v-on 事件监听器传递给
    ⼦组件,⼦组件可以通过访问 $listeners 来⾃定义监听器。 .sync 属性是个语法糖,可以很简单的实现⼦组件与⽗组件通信

为何 Vue.js 采用异步渲染?

答:因为如果不采用异步更新,那么每次更新数据都会对当前组件进行重新渲染. 所以为了性能考虑。Vue会在本轮数据更新后,再去异步更新视图!
有点类似节流的原理
update () {
/* istanbul ignore else */
if (this.lazy) {
this.dirty = true
} else if (this.sync) {
this.run()
} else {
queueWatcher(this); // 当数据发生变化时会将watcher放到一个队列中批量更新
}
}
export function queueWatcher (watcher: Watcher) {
const id = watcher.id // 会对相同的watcher进行过滤
if (has[id] == null) {
has[id] = true
if (!flushing) {
queue.push(watcher)
} else {
let i = queue.length - 1
while (i > index && queue[i].id > watcher.id) {
i–
}
queue.splice(i + 1, 0, watcher)
}
// queue the flush
if (!waiting) {
waiting = true
if (process.env.NODE_ENV !== ‘production’ && !config.async) {
flushSchedulerQueue()
return
}
nextTick(flushSchedulerQueue) // 调用nextTick方法 批量的进行更新
}
}
}

Vue.js 中 v-if 和 v-show 的区别?

答: v-if如果条件不成立不会渲染当前指令所在节点的dom元素 v-show只是切换当前dom的显示或者隐藏 v-show 只是在
display: none 和 display: block 之间切换。⽆论初始条件是什么 都会被渲染出来,后⾯只需要切换 CSS ,
DOM 还是⼀直保留着的。所以总的来说 v- show 在初始渲染时有更⾼的开销,但是切换开销很⼩,更适合于频繁切换的场景。 v-if
的话就得说到 Vue 底层的编译了。当属性初始为 false 时,组件就不会被渲 染,直到条件为 true
,并且切换条件时会触发销毁/挂载组件,所以总的来说在切换时开 销更⾼,更适合不经常切换的场景。 并且基于 v-if
的这种惰性渲染机制,可以在必要的时候才去渲染组件,减少整个⻚⾯的 初始渲染开销。 总结: v-if 按照条件是否渲染, v-show 是
display 的 block 或 none ;
const VueTemplateCompiler = require(‘vue-template-compiler’);
let r1 = VueTemplateCompiler.compile(<div v-if="true"><span v-for="i in 3">hello</span></div>);
/**
with(this) {
return (true) ? _c(‘div’, _l((3), function (i) {
return _c(‘span’, [_v(“hello”)])
}), 0) : _e()
}
/
const VueTemplateCompiler = require(‘vue-template-compiler’);
let r2 = VueTemplateCompiler.compile(<div v-show="true"></div>);
/
*
with(this) {
return _c(‘div’, {
directives: [{
name: “show”,
rawName: “v-show”,
value: (true),
expression: “true”
}]
})
}
*/
// v-show 操作的是样式 定义在platforms/web/runtime/directives/show.js
bind (el: any, { value }: VNodeDirective, vnode: VNodeWithData) {
vnode = locateNode(vnode)
const transition = vnode.data && vnode.data.transition
const originalDisplay = el.__vOriginalDisplay =
el.style.display === ‘none’ ? ‘’ : el.style.display
if (value && transition) {
vnode.data.show = true
enter(vnode, () => {
el.style.display = originalDisplay
})
} else {
el.style.display = value ? originalDisplay : ‘none’
}
}

Vue.js中为什么 v-for 和 v-if 不能连用?

答:v-for会比v-if的优先级高一些,如果连用的话会把v-if给每个元素都添加一下,会造成性能问题
const VueTemplateCompiler = require(‘vue-template-compiler’);
let r1 = VueTemplateCompiler.compile(<div v-if="false" v-for="i in 3">hello</div>);
/**
with(this) {
return _l((3), function (i) {
return (false) ? _c(‘div’, [_v(“hello”)]) : _e()
})
}
*/
console.log(r1.render);

Vue.js 中实现 hash路由和history路由?

答: onhashchange history.pushState hash 模式:在浏览器中符号 “#” ,#以及#后⾯的字符称之为
hash ,⽤ window.location.hash 读取。特点: hash 虽然在 URL 中,但不被包括在 HTTP
请求中;⽤来指导浏览器动作,对服务端安全⽆⽤, hash 不会重加载⻚⾯。 history 模式:h istory 采⽤ HTML5
的新特性;且提供了两个新⽅法:pushState() , replaceState() 可以对浏览器历史记录栈进⾏修改,以及
popState 事件的监听到状态变更

Vue.js 的 action 和 mutation 区别?

答: mutation是同步更新数据(内部会进行是否为异步方式更新数据的检测) action
异步操作,可以获取数据后调佣mutation提交最终数据

Vue.js 和 react 区别? 答:相同点 都⽀持 ssr ,都有 vdom ,组件化开发,实现 webComponents
规范,数据驱 动等 不同点 vue 是双向数据流(当然为了实现单数据流⽅便管理组件状态, vuex 便出现了), react 是单向数据流。
vue 的 vdom 是追踪每个组件的依赖关系,不会渲染整个组件树, react 每当应该状态被改变时,全部⼦组件都会 re-render

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值