美文网首页
Vue.js 重点

Vue.js 重点

作者: Sommouns | 来源:发表于2019-10-31 14:15 被阅读0次

    Vue的生命周期

    • beforeCreate
      实例组件刚创建,元素DOM和数据都还没有初始化,暂时不知道能在这个周期里面进行生命操作。

    • created
      数据data已经初始化完成,方法也已经可以调用,但是DOM为渲染。在这个周期里面如果进行请求是可以改变数据并渲染,由于DOM未挂载,请求过多或者占用时间过长会导致页面线上空白。

    • beforeMount
      DOM未完成挂载,数据也初始化完成,但是数据的双向绑定还是显示{{}},这是因为Vue采用了Virtual DOM(虚拟Dom)技术。先占住了一个坑。

    • mounted
      数据和DOM都完成挂载,在上一个周期占位的数据把值给渲染进去。一般请求会放在这个地方,因为这边请求改变数据之后刚好能渲染。

    • beforeUpdate
      只要是页面数据改变了都会触发,数据更新之前,页面数据还是原来的数据,当你请求赋值一个数据的时候会执行这个周期,如果没有数据改变不执行。

    • updated
      只要是页面数据改变了都会触发,数据更新完毕,页面的数据是更新完成的。beforeUpdate和updated要谨慎使用,因为页面更新数据的时候都会触发,在这里操作数据很影响性能和容易死循环。

    • beforeDestroy
      这个周期是在组件销毁之前执行,在我项目开发中,觉得这个其实有点类似路由钩子beforeRouterLeave,都是在路由离开的时候执行,只不过beforeDestroy无法阻止路由跳转,但是可以做一些路由离开的时候操作,因为这个周期里面还可以使用data和method。比如一个倒计时组件,如果在路由跳转的时候没有清除,这个定时器还是在的,这时候就可以在这个里面清除计时器。

    • Destroyed
      说实在的,我还真的不知道这个周期跟beforeDestroy有什么区别,我在这个周期里面调用data的数据和methods的方法都能调用,所以我会觉得跟beforeDestroy是一样的。


    数据双向绑定

    Object.defineProperty是ES5新增的一个API,其作用是给对象的属性增加更多的控制
    Object.defineProperty(obj, prop, descriptor)
    参数 obj: 需要定义属性的对象(目标对象)
    prop: 需被定义或修改的属性名(对象上的属性或者方法)
    对于setter和getter,我的理解是它们是一对勾子(hook)函数,当你对一个对象的某个属性赋值时,则会自动调用相应的setert函数;而当获取属性时,则调用getter函数。这也是实现双向数据绑定的关键。
    代码实现:

    <body>
      <div id="app">
        <input type="text" id="txt">
        <p id="show-txt"></p>
      </div>
    </body>
    <script>
      var obj = {}
      Object.defineProperty(obj, 'txt', {
        set: function (val) {
          document.getElementById('txt').value = val
          document.getElementById('show-txt').innerHTML = val
        }
      })
    
      document.addEventListener('keyup', function (e) {
        obj.txt = e.target.value
      })
    </script>
    

    Vue父子组件传递参数

    第一种就是普通的

    // parent
    <template>
      <child name="son" @changeValue="changeValue"></child>
    </template>
    <script>
    module.exports = {
      methods: {
        changeValue(val) {
          console.log(val)
        }
      }
    }
    </script>
    
    // child
    <script>
    module.exports = {
      props:{
        name: {
          required: true,
          type: String
        }
      },
      data: {},
      methods: {
        changeValue() {
          this.$emit('changeValue', '123')
        }
      }
     }
    </script>
    

    第二种是我用的比较多的.sync,因为Vue规定prop是不能直接修改的,默认是传值,类似形参

    // parent
    <template>
      <child name.sync="son"></child>
    </template>
    <script>
    module.exports = {
      methods: {
        changeValue(val) {
          console.log(val)
        }
      }
    }
    </script>
    
    // child
    <script>
    module.exports = {
      props:{
        name: {
          required: true,
          type: String
        }
      },
      data: {},
      methods: {
        changeValue() {
          this.$emit('update:name', '123')
        }
      }
     }
    </script>
    

    Vue路由传递参数方法

    • 通过name传值
    routes: [
        {
          path: '/Message',
          name: 'Message',
          component: resolve => require(['../components/page/Message.vue'], resolve)
        }
    ]
    
    // vue页面中
    this.$router.push({name: 'Message',  params: {pa: aa})
    
    // 接收页面
    console.log(this.$route.params.pa)
    
    • 通过<router-link>
    <router-link :to="{name:'hi1',params:{username:'jspang'}}">Hi页面1</router-link>
    
    {path:'/hi1',name:'hi1',component:Hi1}
    
    {{$route.params.username}}
    
    • 利用url传递参数
    {
         path:'/params/:newsId/:newsTitle',
         component:Params
    }
    <router-link to="/params/198/jspang website is very good">params</router-link>
    

    Vue自定义组件


    Vue自定义指令

    // 和自定义过滤器一样,我们这里定义的是全局指令
    Vue.directive('focus',{
        inserted(el) {
          el.focus()
        }
    })
    
    <div id='app'>
        <input type="text">
        <input type="text" v-focus placeholder="我有v-focus,所以,我获取了焦点">
    </div>
    

    这里放了两个 input ,但是后面的 input 才使用了我们的自定义 v-focus 指令,所以看到了是后面那个文本框获取了焦点,而不是前面一个。

    看到上面这个例子,可以总结几点

    • 使用 Vue.directive() 来新建一个全局指令,(指令使用在HTML元素属性上的)
    • Vue.directive('focus') 第一个参数focus是指令名,指令名在声明的时候,不需要加 v-
    • 在使用指令的HTML元素上,<input type="text" v-focus placeholder="我有v-focus,所以,我获取了焦点"/> 我们需要加上 v-.
    • Vue.directive('focus',{}) 第二个参数是一个对象,对象内部有个 inserted() 的函数,函数有 el 这个参数.
    • el 这个参数表示了绑定这个指令的 DOM元素,在这里就是后面那个有 placeholder 的 input
    • el 就等价于 document.getElementById('el.id')....可以利用 $(el) 无缝连接 jQuery

    下面说下指令的生命周期

    Vue.directive('gqs',{
        bind() {
          // 当指令绑定到 HTML 元素上时触发.**只调用一次**
          console.log('bind triggerd')
        },
        inserted() {
          // 当绑定了指令的这个HTML元素插入到父元素上时触发(在这里父元素是 `div#app`)**.但不保证,父元素已经插入了 DOM 文档.**
          console.log('inserted triggerd')
        },
        update() {
          // 所在组件的`VNode`更新时调用.
          console.log('updated triggerd')
        },
        componentUpdated() {
          // 指令所在组件的 VNode 及其子 VNode 全部更新后调用。
          console.log('componentUpdated triggerd')
          
        },
        unbind() {
          // 只调用一次,指令与元素解绑时调用.
          console.log('unbind triggerd')
        }
      })
    

    vuex工作原理和组成

    首先要说的是,vuex作为一个插件,在vue中使用,就会调用他的install方

    // src/store.js
    export function install (_Vue) {
      if (Vue && _Vue === Vue) {
        return
      }
      Vue = _Vue
      applyMixin(Vue)
    }
    

    其代码比较简单,调用了一下applyMixin方法,该方法主要作用就是在所有组件的beforeCreate生命周期注入了设置this.$store这样一个对象

    下面来看一下他的构造函数

    // src/store.js
    constructor (options = {}) {
      const {
        plugins = [],
        strict = false
      } = options
    
      // store internal state
      this._committing = false
      this._actions = Object.create(null)
      this._actionSubscribers = []
      this._mutations = Object.create(null)
      this._wrappedGetters = Object.create(null)
      this._modules = new ModuleCollection(options)
      this._modulesNamespaceMap = Object.create(null)
      this._subscribers = []
      this._watcherVM = new Vue()
    
      const store = this
      const { dispatch, commit } = this
      this.dispatch = function boundDispatch (type, payload) {
        return dispatch.call(store, type, payload)
      }
      this.commit = function boundCommit (type, payload, options) {
        return commit.call(store, type, payload, options)
      }
    
      // strict mode
      this.strict = strict
    
      const state = this._modules.root.state
    
      // init root module.
      // this also recursively registers all sub-modules
      // and collects all module getters inside this._wrappedGetters
      installModule(this, state, [], this._modules.root)
    
      // 重点方法 ,重置VM
      resetStoreVM(this, state)
    
      // apply plugins
      plugins.forEach(plugin => plugin(this))
    
    }
    

    除了一堆初始化外,我们注意到了这样一行代码resetStoreVM(this, state)他就是整个vuex的关键

    // src/store.js
    function resetStoreVM (store, state, hot) {
      // 省略无关代码
      Vue.config.silent = true
      store._vm = new Vue({
        data: {
          $$state: state
        },
        computed
      })
    }
    

    去除了一些无关代码后我们发现,其本质就是将我们传入的state作为一个隐藏的vue组件的data,也就是说,我们的commit操作,本质上其实是修改这个组件的data值,结合上文的computed,修改被defineReactive代理的对象值后,会将其收集到的依赖的watcher中的dirty设置为true,等到下一次访问该watcher中的值后重新获取最新值。

    这样就能解释了为什么vuex中的state的对象属性必须提前定义好,如果该state中途增加一个属性,因为该属性没有被defineReactive,所以其依赖系统没有检测到,自然不能更新。

    由上所说,我们可以得知store._vm.$data.$$state === store.state, 我们可以在任何含有vuex框架的工程验证这一点。

    后一句话结束vuex工作原理,vuex中的store本质就是没有template的隐藏着的vue组件


    Vue-Router的原理

    使用

    const routes = [
      {
        path: '/',
        redirect: '/recommend'
      },
      {
        path: '/recommend',
        component: () => import('../components/recommend/view.vue')
      },
      {
        path: '/singer',
        component: () => import('../components/singer/view.vue')
      },
      {
        path: '/rank',
        component: () => import('../components/rank/view.vue')
      },
      {
        path: '/search',
        component: () => import('../components/search/view.vue')
      }
    ]
    export default routes
    
    import Vue from 'vue'
    import Router from 'vue-router'
    import routes from './routes'
    
    Vue.use(Router)
    
    export default new Router({
      // mode: 'history',
      routes
    })
    

    vue-router通过hash与History interface两种方式实现前端路由,更新视图但不重新请求页面”是前端路由原理的核心之一,目前在浏览器环境中这一功能的实现主要有两种方式

    1. hash ---- 利用URL中的hash(“#”)
    2. 利用History interface在 HTML5中新增的方法

    那么,我们要选择用哪种方式呢?
    在vue-router中,它提供mode参数来决定采用哪一种方式,选择流程如下:
    mode 参数:

    默认hash
    history 注:如果浏览器不支持history新特性,则采用hash方式
    如果不在浏览器环境则使用abstract(node环境下)

    mode: 'hash'

    http://localhost:8080/#/recommend
    

    mode: 'history'

    http://localhost:8080/recommend
    
     // 根据mode确定history实际的类并实例化    
    
    // 根据mode确定history实际的类并实例化
    switch (mode) {
      case 'history':
        this.history = new HTML5History(this, options.base)
        break
      case 'hash':
        this.history = new HashHistory(this, options.base, this.fallback)
        break
      case 'abstract':
        this.history = new AbstractHistory(this, options.base)
        break
      default:
        if (process.env.NODE_ENV !== 'production') {
          assert(false, `invalid mode: ${mode}`)
        }
    }
    
    HashHistory

    HashHistory真是身怀绝技,会很多东西。特别是替换路由特别厉害。还可以通过不同的方式,一个是push,一个是replace.

    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() //更新视图
    
    HTML5History

    History interface是浏览器历史记录栈提供的接口,通过back(), forward(), go()等方法,我们可以读取浏览器历史记录栈的信息,进行各种跳转操作。
    从HTML5开始,History interface有进一步修炼:pushState(), replaceState() 这下不仅是读取了,还可以对浏览器历史记录栈进行修改


    vue的seo问题

    由于传统的搜索引擎只会从 HTML 中抓取数据,导致前端渲染的页面无法被抓取。前端渲染常使用的 SPA 会把所有 JS 整体打包,无法忽视的问题就是文件太大,导致渲染前等待很长时间。特别是网速差的时候,让用户等待白屏结束并非一个很好的体验。

    解决方案:

    • vue ssr
    • nuxt.js
    • prerender-spa-plugin插件
      由于项目只是改善少数页面的seo,所以使用预渲染方式,预渲染插件使用vue官方推荐的prerender-spa-plugin

    生命周期内create和mounted的区别

    created:在模板渲染成html前调用,即通常初始化某些属性值,然后再渲染成视图。
    mounted:在模板渲染成html后调用,通常是初始化页面完成后,再对html的dom节点进行一些需要的操作。

    其实两者比较好理解,通常created使用的次数多,而mounted通常是在一些插件的使用或者组件的使用中进行操作,比如插件chart.js的使用: var ctx = document.getElementById(ID);通常会有这一步,而如果你写入组件中,你会发现在created中无法对chart进行一些初始化配置,一定要等这个html渲染完后才可以进行,那么mounted就是不二之选。


    Vue实现登陆拦截

    Step1: requireAuth属性
    requireAuth属性作用是表明该路由是否需要登录验证,在进行全局拦截时,我们将通过该属性判断路由的跳转,该属性包含在meta属性中:

    routes = [
        {
            name: 'detail',
            path: '/detail',
            meta: {
                requireAuth: true
            }
        },
        {
            name: 'login',
            path: '/login'
        }
    ]
    

    Step2: router.beforeEach

    router.beforeEach((from, to, next) => {
        if (to.meta.requireAuth) { // 判断跳转的路由是否需要登录
            if (store.state.token) { // vuex.state判断token是否存在
                next() // 已登录
            } else {
                next({
                    path: '/login',
                    query: {redirect: to.fullPath} // 将跳转的路由path作为参数,登录成功后跳转到该路由
                })
            }
        } else {
           next()
        }
    })
    

    相关文章

      网友评论

          本文标题:Vue.js 重点

          本文链接:https://www.haomeiwen.com/subject/mzgavctx.html