vue2项目详解技术点

前言:vue2所用到的技术栈

1:vue/cli脚手架

2:vue2.0

        a:创建vue项目(vue create 项目名称)

3:vant(移动端组件库)、elementul(pc端组件库)

4:vue-router--3(使用3的版本)

5:vuex--3(使用vuex3的版本)


一:vue基础   

1:指令(常用)

        a:v-model

        b:v-text

        c:v-html

        d:v-bind / :

        e:v-on / @

        f:v-if

        g:v-show

        h:v-for

        i:v-slot / #

        j:{{}}

2:选项

        a:data

        b:computed

        c:watch

        d:methods

export default {
    data(){
        return {
        }
    },
    computed:{
    
    },
    watch:{
    },
    methods:{
    }
}

3:自定义组件(局部/全局)

  • components(局部)component(全局)
    • 例:
    • //所有组件导入必须注册
      import vue from 'vue'
      import helloworld from './路径'    //从别处导入自定义组件
      
      //全局注册
      vue.component('helloworld',helloworld)
      
      
      //局部注册
      
      export default{
      
          data(){
              return {
      
              }
          },
          components:{
              helloworld    //注册
          }
      }

4:过度动画(还待研究)

5:混入

        mixins({},{})(局部)、mixin({选项})(全局)

import vue from 'vue'
vue.mixin({
    data(){
        return {
        }
    },
    compputed:{
    }
})



6:过滤器

filters(局部)/ filter(全局)

        

Vue.filter('rmb', val=>{
      // do something
      return '¥'+Number(val).toFixed(2)
    })

    const app = new Vue({
      el: '#app',
      data: {
        price: 99.9,
        num: 202210923920
      },
      filters: {
        'qf' (val) {
          return 'QF:'+val
        },
        'sz' (val) {
          return 'SZ:' + val
        }
      }
    })

7:自定义指令

        directives(局部) / directive(全局)

    Vue.directive('form', {
      bind (el, binding, vnode) {
        // console.log('---el', el)
        // console.log('---binding', binding)
        // console.log('---vnode', vnode)
        const { context } = vnode
        const { expression, modifiers } = binding
        const { lazy, trim, number, upper } = modifiers
        // v-bind:value
        el.value = context[expression]
        // v-on:input
        el.addEventListener(lazy?'blur':'input', function(ev){
          // console.log('---事件', ev.target.value)
          let val = ev.target.value
          // if (trim) val = val.trim()
          // if (number) val = Number(val)
          // if (upper) val = val.toUpperCase()
          context[expression] = val  // 必须使用上下文来双向绑定
        })
      },
      update (el, binding, vnode) {
        el.value = vnode.context[binding.expression]
      }
    })

    const app = new Vue({
      el: '#app',
      data: {
        cc: 'green',
        name: '张三'
      },
      directives: {
        // fn写法,等价于 bind + update这两个钩子
        'color': function (el, binding, vnode) {
          // console.log('---el', el)
          // console.log('---binding', binding)
          // console.log('---vnode', vnode)
          el.style.color = binding.value
        },

        // 'color': {
        //   bind (el, binding) {
        //     el.style.color = binding.value
        //   },
        //   update (el, binding) {
        //     el.style.color = binding.value
        //   },
        //   inserted () {
        //     console.log('---inserted')
        //   }
        // }
      }
    })

8:插件

        

    const QfPlugin = {
      install (Vue) {
        Vue.mixin({ data() { return { version: 'v2' }}})
        Vue.component('qf-button', {
          template: `
          <div>我的按钮</div>
          `
        })
        Vue.filter('rmb', val=>('¥'+val))
        Vue.directive('color', function(el,binding){ el.style.color = binding.value})
        Vue.prototype.$ajax= function(url,method,data) { console.log('---调接口') }
      }
    }

    const SzPlugin = function (Vue) {
      Vue.prototype.$msg = '来了就是深圳打工人'
    }

    Vue.use(QfPlugin) // 注册插件(调用插件上的install()方法,并传入Vue实参)
    Vue.use(SzPlugin) // 如果插件的类型是Function,直接调用并传入Vue实参

    const app = new Vue({
      mounted () {
        console.log('--version', this.version)
        console.log('--$ajax', this.$ajax)
        console.log('--$msg', this.$msg)
      }
    })
    app.$mount('#app')

9:两个重要API

        

   // 1、Vue.nextTick() / this.$nextTick()
    // 事实:set操作,代码确实是同步的,但是set行为是异步的;set操00作修改声明变量,触发re-render生成新的虚拟DOM,进一步执行diff运算,找到脏节点集合,交给Vue背后的更新队列去执行循环更新。
    // 什么是nextTick?在更新队列中每一个更新任务都是一个更新单元,nextTick表示下一个更新单元(更新周期)。
    // 作用:这么描述一个场景,我们set操作data(更新DOM),你希望访问这个DOM的最新状态时,使用this.$nextTick(handler)。
    // nextTick() 是某种程度(场景)下,可以使用 updated() 来替代;原则上建议使用 nextTick()。

    // 2、this.$forceUpdate()
    // 事实:Vue响应式是有缺陷的,什么缺陷?在复杂的Vue应用中,如果声明式变量是引用数据类型,当你set操作这些复杂的引用数据类型时,视图不更新。解决方案,set操作完成后,立即调用 this.$forceUpdate()强调更新(强制re-render)。
    // 有时候,this.$forceUpdate()也无法解决上述问题,对set操作的变量进行一行深复制。

    // 3、面试题
    // 谈一谈你对 Vue.nextTick() 的理解?有什么用?(在nextTick访问最新的DOM)
    // nextTick() 和 updated() 的区别 (前者只是表示一个更新单元已完成,后者是生命周期钩子表示整个页面更新完成)
    // Vue响应式有没有缺陷呢?有什么缺陷?遇到这种问题你会怎么办?
    // 什么是深拷贝?什么是浅拷贝?有哪些深拷贝的方法?让你手写一个深拷贝方法,你会怎么写?
    const app = new Vue({
      el: '#app',
      data () {
        return {
          num: 1,
          content: '',
          info: {
            user: {
              children: [
                { name: '张三', age: 10 }
              ]
            }
          }
        }
      },
      mounted () {
        // 模拟调接口
        setTimeout(()=>{
          const res = '<div><a id="a" href="https://baidu.com">百度</a></div>'
          this.content = res
          // 上面这个set行为异步更新完成后,执行nextTick的回调
          Vue.nextTick(()=>{
            document.getElementById('a').style.color = 'black'
          })
        }, 200)
      },
      methods: {
        add () {
          this.num++
          this.num++
          this.$nextTick(()=>{
            const tt = document.getElementById('h1').innerText
            console.log('--tt', tt)
          })
        },
        change () {
          this.info.user.children[0].age++
          // this.$forceUpdate()
          // this.info = JSON.parse(JSON.stringify(this.info))
          // this.info = _.cloneDeep(this.info)
        }
      }
    })

10:组件通信

        

 // 通信是组件或模块之间的数据交互。多重通信就形成了数据流,数据流管理的优劣决定了产品能否上线。数据流(通信)越混乱,代码越难维护。
6
    // 2、Vue中有哪些常见的通信方案?

    // (*1*)父子组件通信:父传子使用自定义属性(props),子传父使用自定义事件($emit())。

    // (*2*)状态提升:当兄弟组件之间需要共享数据时,我们通常的做法是把这个数据定义它们的共同的父组件中,再通过自定义属性实现数据共享。

    // (*3*)provide/inject:这是在组件树中,自上而下的一种数据通信方案,也就是说只能父级组件中向后代组件传递。需要注意的是,当provide提供动态数据(声明式变量)时,动态数据发生变化,后代组件们不会自动更新。这是为什么呢?你自己从生命周期流程的角度去思考。

    // (4)ref通信:ref是Vue内置的一个属性,每一个HTML元素或组件都有这个属性;ref作用在HTML元素上得到DOM实例,ref作用在组件上得到组件实例。使用ref访问组件实例,进一步可以访问组件中的数据和方法。(说明:ref是一种快速的DOM的访问方式,当然ref也可作用在组件上得到组件实例。这些ref得到的DOM实例或组件实例,使用this.$refs来访问它们。ref尽量少用,除非某些难搞的需求。)

    // (*5*)插槽通信:借助<slot>组件实现从子组件向父组件传递数据,借助this.$slots访问父组件中的插槽实例。(在自定义组件中使用this.$slots访问父组件给的插槽实例;在父组件插槽中使用#default='scoped'访问子组件<slot>回传的数据。这种通信在组件库中、工作中,非常常见!)

    // (6)$parent/$children:借助$parent/$children可以实现,】件实例,可以做到在组件中随意穿梭。($parent表示的是当前组件的父组件实例,$children表示的是当前组件的子组件们。)

    // (7)$attrs/$listeners:借助$attrs可访问父组件传递过来的自定义属性(除了class和style外),借助$listenrs可以访问父组件给的自定义事件。在某些场景下,$attrs/$listeners可以替代props/$emit()这种通用的通信方案。

    // (*8*)事件总线:借助于Vue内置的事件系统($on/$emit/$off/$once)实现“订阅-发布”式的通信,这种通信方式是一种与组件层级无关的“一对多”的通信。(工作中很少用,一些特殊的Vue项目才用得到事件总线。)

    // (9)Vuex通信:这是Vue架构中终极的通信方案,也是Vue架构中用的最多的一种通信方案。

    Vue.component('qf-child-a', {
      template: `
      <div>
        <div>大哥 {{num}}</div>
        <button @click='$emit("add", 1)'>加1</button>
      </div>
      `,
      props: {
        num: { type: Number, default: 0 }
      }
    })

    Vue.component('qf-child-b', {
      template: `
      <div>
        <div>二哥 {{num}}</div>
        <button @click='$emit("sub", 2)'>减2</button>
      </div>
      `,
      props: {
        num: { type: Number, default: 0 }
      }
    })

    Vue.component('qf-child-c', {
      template: `
      <div>
        <div>三哥 - {{msg}} - {{num}}</div>
        <span v-for='i in list' v-text='i'></span>
      </div>
      `,
      // 从组件树上下文中注入provide数据
      inject: ['msg', 'list', 'num']
    })

    Vue.component('qf-child-d', {
      template: `
      <div>
        <div>四哥:{{age}}</div>
      </div>
      `,
      data () {
        return {
          age: 10
        }
      },
      methods: {
        changeAge (arg) {
          this.age = arg || 10
        }
      }
    })

    Vue.component('qf-child-e', {
      template:`
      <div>
        <div>五哥</div>
        <slot name='default' foo='你好' bar='世界'>
          <div>我的房子</div>
        </slot>
        <slot name='car' :car='car'>
          <div>我的车位</div>
        </slot>
      </div>
      `,
      data () {
        const cars = ['劳斯莱斯','奔驰','宝马']
        const val = cars[Math.floor(Math.random()*3)]
        return {
          car: val
        }
      },
      mounted () {
        // 在子组件中,使用this.$slots来访问父组件给的插槽内容
        console.log('--slots', this.$slots)
      }
    })

    Vue.component('qf-child-f', {
      template: `
      <div>
        <div>六哥 {{num}}</div>

      </div>
      `,
      data () {
        return {
          num: 1
        }
      },
      methods: {
        add () {
          this.num++
        }
      }
    })

    Vue.component('qf-child-g', {
      template: `
      <div class='page'>
        <div>七哥</div>
        <span v-for='i in pageArr' v-text='i' :class='{on:$attrs.page===i}' @click='$listeners.change(i)'></span>
      </div>
      `,
      mounted () {
        console.log('g---$attrs', this.$attrs)
        console.log('g---$listeners', this.$listeners)
      },
      computed: {
        pageArr () {
          const p = this.$attrs.page
          if (p<=3) return [1,2,3,4,5]
          else return [p-2,p-1,p,p+1,p+2]
        }
      }
    })

    const app = new Vue({
      el: '#app',
      data () {
        // do something
        return {
          num: 1,
          page: 1
        }
      },
      // 从当前组件节点开始,向后代组件们传递数据
      // provide: {
      //   msg: '你好',
      //   list: [1,2,3,4]
      // },
      provide () {
        // do something
        return {
          msg: '你好',
          list: [1,2,3,4], // 静态数据
          num: this.num    // 动态数据
        }
      },
      methods: {
        handle () {
          console.log('---refs', this.$refs)
          this.$refs.box.style.color = 'red'
          this.$refs.dd.changeAge(20)
        },
        changePage (arg) {
          this.page = arg || 1
        }
      },
      mounted () {
        console.log('app---$parent', this.$parent)
        console.log('app---$children[5]', this.$children[5])
      }
    })







 // 1、事件总线 const bus = new Vue()
    // bus.$on('频道', callback)    监听一个“频道”
    // bus.$emit('频道', '消息')  向指定“频道”上发送消息
    // bus.$off()   取消订阅某个“频道”
    // bus.$once()  对某个“频道”只监听一次

    // 2、什么是“订阅-发布”模式?
    // 这里的事件总线就是一种“订阅-发布”模式;Vue源码中的Dep类也是一种“订阅-发布”模式。
    // “订阅-发布”模式,也叫做“观察者模式”。

    const bus = new Vue()  // 事件总线(事件系统)

    Vue.component('qf-teacher', {
      template: `
      <div>
        <h2>老师在线</h2>
        <input type="text" v-model='msg' @keyup.enter='send' />
        <button @click='send'>发送</button>
        <div v-html='content'></div>
      </div>
      `,
      data () {
        return {
          msg: '',
          content: ''
        }
      },
      mounted () {
        bus.$on('qfstu', msg => {
          this.content += `<div>学生说:${msg}</div>`
        })
      },
      methods: {
        send () {
          // 使用事件总线向 qf 频道上发送消息
          bus.$emit('qf', this.msg)
          this.msg = ''
        }
      }
    })

    Vue.component('qf-student', {
      template: `
      <div>
        <h2>学生在线</h2>
        <input type="text" v-model='msg' @keyup.enter='send' />
        <button @click='send'>发送</button>
        <div v-html='content'></div>
      </div>
      `,
      data () {
        return {
          msg: '',
          content: ''
        }
      },
      mounted () {
        // 使用事件总线,监听qf这个频道
        bus.$on('qf', msg=>{
          console.log('student--qf频道上有消息来了', msg)
          this.content += `<div class='row'>老师说:${msg}</div>`
        })
      },
      methods: {
        send () {
          bus.$emit('qfstu', this.msg)
          this.msg = ''
        }
      }
    })

    const app = new Vue({
      el: '#app'
    })

11:生命周期

创建阶段:beforeCreate、created(**)

挂载阶段:beforeMount、mounted(**)

更新阶段:beforeUpdate、updated

销毁阶段:beforeDestroy(**)、destroyed


二:vue-router

  1. - 如何在脚手架环境中集成Vue路由系统?(vue(2) + vue-router(3))

      - 第一步:安装路由v3版本,注册路由

        - cnpm i vue-router@3.5.4 -S  // @用于指定版本、-S表示安装成功后把这个包记录在package.json的“dependencies”中。

        - 新建src/router.js文件,注册路由Vue.use(VueRouter)

      - 第二步:创建路由实例、定义路由规则,并在main.js挂载路由系统

        - export default new VueRouter({mode, routes:[]})

        - 在main.js挂载路由 new Vue({ router })

      - 第三步:在合适的位置放置一个视图容器和菜单

        - 在App.vue的视图中放置一个 <router-view>显示url匹配成功的组件。

        - 在App.vue的视图中使用 <router-link>制作菜单,点击跳转url。

  2. 路由模式

    1. hash路由:#,背后监听onhashchange事件实现,部署上线不会出现404

    2. history路由:背后基于history api 实现,部署上线会出现404问题

  3. 全局组件

    1. <router-link>

      1. to属性跳转目标

      2. tag属性用于最终渲染什么标签(默认时a标签)

    2. <router-view>

      1. name属性用于制定命令视图

  4. 两个内置API

    1. $route:表示路由信息

      1. this.$route.fullPath

      2. this.$router.query

      3. this.$route.params

      4. this.$router.meta

    2. $router:表示路由跳转

      1. this.$router.push():路由跳转

      2. this.$router.replace():向前跳转

      3. this.$router.back():返回上一级

  5. 路由跳转

    1. 声明式跳转

      1. <router-link>方式跳转

    2. 编程时跳转

      1. $router方式跳转

  6. 路由传参

    1. query传参:在跳转路由的url后面用?a=1&b=2&c=3这种方式传参,在另一个组件中使用this.$route.query接收。

    2. 动态路由传参:像这样 `{path: '/good/:id', component }`定义路由规则,在这条动态路由规则对应的组件中使用this.$route.params接收,或者开启props:true后使用 props选项来接收。

  7. 命名视图与命名路由

    1. 所谓的命名视图,意思是给<router-view>加一个name属性。

    2. 所谓的命名路由,意思是给{path,component}路由规则取一个名字。

  8. 两个优化:路由懒加载、重定向与别名

    1.  路由懒加载:当一个SPA应用程序中的页面足够多,我们需要根据路由系统进行按需加载组件(而不是一次性加载所有组件),该怎么实现呢?使用路由懒加载(背后原理是Webpack代码分割技术、Vue异步组件)。路由懒加载,是一种性能优化方案。

    2. 重定向与别名:当用户访问一个未定义的路由时,给一个重定向(跳转到另一个已定义的路由上),这是一种用户体验的优化。重定向规则,一般要放在路由规则的最后面。什么是别名?别名是path的简写,可以用于路由访问;什么时候需要用到别名?当path比较复杂时,需要给它设计一个别名。

  9. 两个难点:嵌套视图(嵌套路由)、导航守卫(路由元信息)

    1. 嵌套视图(嵌套路由):当我们设计类似知乎官网那样的一级菜单、二级菜单时,就要用到嵌套视图。所谓“嵌套视图”,从组件树的角度来讲,<router-view>所显示的组件的内部还有<router-view>;从路由规则的角度来讲,{path,component,children}带有children这个属性;从产品设计的角度来讲,一级菜单对应的页面中还有二级菜单。

    2. 导般守卫:在router实例对象上有三个重要的全局钩子(beforeEach、beforeResolve、afterEach),每次url发生变化时,都会触发这三个钩子按顺序执行。那么以后我可以在这些钩子编写验证逻辑,如果验证通过就放你过去,你就可以正常访问你想访问的页面;如果验证失败,就阻止你访问目标页面,这就实现“守卫”的效用了。在路由中,使用导航守卫和路由元信息,可以做鉴权、还可以做权限设计。


三:vuex

  1. 如何在脚手架环境中集成Vuex状态管理?

      - 第一步:安装vuex指定版本,并注册Vue.use(Vuex) / cnpm i vuex@3.6.2 -S

      - 第二步:创建store实例{五个概念}并抛出,在main.js挂载store

      - 第三步:在组件中使用this.$store/四个map方法来使用store或走数据流程

  2. 如何安装devtools?devtools有什么用?(用于调试组件树、用于调试Vuex数据流)

  3. 关于axios使用

      - 为了调接口,我们推荐使用axios这个HTTP工具(建议在npm上搜索axios,阅读一下它的文档)

      - 为什么要使用axios?(axios是基于Promise的、在Web浏览器和Node应用中都可以使用)

      - 如何使用axios?第一步:安装并封装(拦截器);第二步:使用封装过的axios实例进行调接口。

  4. 创建store时要用的五个概念(state/getters/mutations/actions/modules)

    1. state: {} 用于定义可被组件共享数据,是具有响应式的;在组件中使用this.$store.state来访问它们。

    2. getters: {fn} 用于计算state,相当于Vue的计算属性,当state发生变化时getters方法自动自动重新计算;在组件中使用this.$store.getters来访问它们。

    3. mutations: {fn} 专门用于修改state的,所以mutations方法是这样fn(state,payload)定义的;mutations方法在actions中或组件中使用,使用$store.commit('mutations方法',payload)来触发。

    4. actions: {fn} 专门用于调接口的,所以actions方法是这样fn(store,payload)定义的;在组件中使用this.$store.dispatch('actions方法', payload)。

    5. modules: {子store} 是一个Vuex架构层面的概念,用于拆分子store。大家在拆分子store务必在子store中使用namespaced:true开启命名空间。

  5. 在组件中推荐使用四个map*方法来访问store数据或操作store。

    1. mapState/mapGetters,必须写在computed计算属性中,用于访问state/getters数据。映射进来后,就可以用this来访问这些数据了。

    2. mapActions/mapMutations 必须写在methods选项中,用于访问mutations/actions方法。映射进来后,可以用this调用这些方法。

    3. 它们的语法是相同的:map*('命名空间', ['k1', 'k2'])

  6. 使用Vuex的几个原则(*)

    1. 原则1:只要使用Vuex一定要拆分store,拆分store后在根store上不要再使用state/mutations/actions

    2. 原则2:在子store务必开启命名空间namespaced:true。

    3. 原则3:在组件中尽可能不要使用$store,建议使用四个map*方法。


  • 1
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值