Vue2

86 阅读4分钟

Vue2

版本:

  • vue 2.x
  • vue-router 3.0.x
  • vue-cli 2.x 3.x

一. Vuejs

MVVM

  • View,视图层(DOM)

  • ViewModel,双向

    • View -> DOM Listeners -> Model,DOM监听
    • View <- Data Bindings <- Model,数据绑定
  • Model,数据层(数据)

mvvm

二. Vue基础语法

1. 插值语法

  • {{}}
  • v-once,只渲染一次,之后不改变
  • v-html
  • v-text

2.绑定属性

  • v-bind

    • v-bind:src="imgUrl"
    • :src="imgUrl"
  • 绑定class

    • :class="{className: boolean}"
    • :class="[变量名]"
  • 绑定style

    • :style="{color: fontColor}"
    • :style="[{color: 'lightpink'}]"

3.计算属性

  • 数据先处理再显示
  • 会进行缓存,多次使用只调用一次
  • 若方法的变量变了,会自动调用

4.事件监听

  • v-on:click="plus" @click="plus" 绑定事件的监听器

  • 参数

    • @click="btnClick" btnClick (event) {}
    • @click="btnClick('params', $event)" btnClick (params, event) {}
    • event参数->浏览器生产的 event 事件
  • 修饰符

    • .stop

      • @click.stop="insideBox"阻止 inside 往 outside 冒泡
    • .prevent

      • 调用 event.stopPropagation(),阻止标签的默认行为
    • .{keyCode | keyAlias}

      • @keyup.enter="keyup"监听键盘的某个键值(键修饰符)
    • once

      • 只触发第一次回调
    • .native

      • 监听组件根元素的原生事件
      • <!-- 父组件 -->
        <template>
            <div>
                <Button text="" icon="" @click.native="onButtonClick" />
            </div>
        </template><!-- 子组件 -->
        <template>
            <button>
                <sapn>{{text}}</sapn>
                <i>{{icon}}</i>
            </button>
        </template><!-- 子组件Button内部(span,i)的原生事件,绑定到组件的根元素上,使父组件可以直接监听Button的原生事件-->
        
原生事件和自定义事件
html
<!DOCTYPE html>
  <html lang="en">
      <head>
          <meta charset="UTF-8">
          <meta http-equiv="X-UA-Compatible" content="IE=edge">
          <meta name="viewport" content="width=device-width, initial-scale=1.0">
          <title>原生事件与自定义事件</title>
      </head>
      <style>
          .boxStyle {
              margin: 20px;
              border-bottom: 1px solid lightgray;
          }
      </style>
      <body>
          <div id="box1" class="boxStyle">
              <!-- 原生事件,在子组件上绑定后可直接使用 -->
              <my-cpn1 @click.native="onClick"></my-cpn1>
              <!-- 自定义事件,在子组件上绑定,要通过子组件的触发$emit才能执行 -->
              <my-cpn1 @onclick="onClick"></my-cpn1>
          </div>
          <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
          <script>
              const component1 = Vue.extend({
                  template: `
                  <div>
                      <h3 @click="onComponentClick">component</h3>
                      <span>span11111111111111</span>
                  </div>
                  `,
                  methods: {
                      onComponentClick() {
                          console.log('子组件被点击')
                          this.$emit('onclick')
                      }
                  }
              })
              Vue.component('my-cpn1', component1)
              
              const box1 = new Vue({
                  el: '#box1',
                  methods: {
                      onClick() {
                          console.log('父组件被点击')
                      }
                  }
              })
          </script>
      </body>
  </html>

5.条件与循环

  • v-if v-else

    • input切换了,input的值还在

      • 基于性能考虑,虚拟DOM在渲染时会尽可能复用存在的元素,给标签加不同的key可避免
      • vue2的diff算法
  • v-show

v-if 和 v-show
  • v-if 为 false 时,元素不会存在 DOM 中

  • v-show 为 false 时,元素添加行内样式:display: none;

  • 显示与隐藏切片频繁时,用 v-show ;只有一次切换时,用 v-if

  • v-for

    • 使用时,加上 :key,给每个节点做一个标识,使其更高效的更新虚拟 DOM
v-if 和 v-for
  • v-for的优先级高于v-if(vue2),v-if高于v-for(vue3)
  • 不用在同一个元素上,避免产生渲染顺序问题和保持代码的可读性和维护性

6.表单绑定

  • v-model

    • 语法糖,实际两操作

      • :value = "message"v-bind 绑定一个 value 属性
      • @input = "message = $event.target.value"v-on 给当前绑定 input 事件

7.其他

  • filters

    • // 通过capitalize过滤器对message的值进行格式化
      <p>{{ message | capitalize}}</p>
      ​
      const vm = new Vue({
          ...,
          filters: {
              capitalize(str) {
                  return str.chatAr(0).toUpperCase + str.slice(1)
              }
          }
      })
      
  • watch

    • 属性

      • immediate
watch 和 computed

三、组件化开发

1.组件

  • data属性

    • data() {return {}}必须是函数,返回一个对象
    • 保证每个实例对象的 data 是互不干扰的,所以 data 需要返回一个独立的新对象
  • 注册组件的基本步骤

    • a. 调用 Vue.extend() 方法创建组件构造器
    • b. 调用 Vue.component() 方法注册组件
    • c. 在 Vue实例的作用范围内使用组件

2.组件间数据传递

  • 父级 -> 子级

    • props
    • 父 <cpn :movies="movies"></cpn>
      子 props
      
    • 不要直接修改 props,用 data 或者 computed 包一层
    • 11
  • 子级 -> 父级

    • $emit
    • 父 <cpn @itemClick="itemClick" />
      子 methods: { btnClick(item) { this.$emit('itemClick', item) } }
      
    • 11
  • 父组件访问子组件

    • this.$children[0],数组对象
    • this.$refs.cpn,对象,子<cpn ref="cpn"></cpn>
  • 子组件访问父组件

    • this.$parent
    • this.$root
  • 非父子组件

3.组件化

  • slot

    • 匿名插槽
    • 具名插槽
    • 作用域插槽:子组件渲染作用域插槽时,可以将子组件内部的数据传递给父组件,让父组件根据子组件的传递过来的数据决定如何渲染该插槽
  • 动态组件 v-bind:component='cpnName'

  • 异步组件 () => import( './cpn' )

组件的生命周期

四、Vue CLI

1.模块化开发

  • CommonJS

    • 导出:module.exports = {one: 1, two: 2}
    • 导入:let {one, two} = require("./a.js")
  • ES6 的 Modules

    • 导出:export
    • 导入:import

2.webpack

  • entry、output
  • loaders(转化器)
  • plugins(扩展器)

3.vue-cli3

4.vue-router

  • 前端路由时代

    • 整个网站只有一个 html 页面,index.html+css+js(全部)
    • 前端管理 URL -> 页面,改变URL,页面不整体刷新(还在同一个网站)
  • 前端路由规则

    • 浏览器URL的hash

    • HTML5的history

      • www.baidu.com/aaa
      • 栈结构,入栈history.pushState({}, " ", 'aaa'),出栈history.back()
      • 替换网站,无历史,history.replaceState({}, " ", 'aaa')
      • 发送请求
  • route-view

    • // router.js
      import Vue from 'vue'
      import Router from 'vue-router'// a. 安装 vue-router 插件
      Vue.use(VueRouter)
      // b.配置路由和组件的映射关系
      const router = new VueRouter({})
      ​
      // main.js
      // c.在 Vue 实例上挂载路由实例
      new Vue({
        router,
        render: h => h(App)
      }).$mount('#app')
      ​
      // App.vue
      // d.使用路由
      <router-link to="/home">路由跳转,a标签</router-link>
      this.$router.push('/home') // js路由跳转
      <div id="app">
          <keep-alive exclude="Detail">
              <router-view/>  // 组件内容显示 
          </keep-alive>
          <MainTabBar></MainTabBar>
      </div>
      
history 和 hash 的对比
- history,会发送请求;单页应用进行刷新时,需要服务端添加回退路由
- hash,不会发送请求;不足:css中的#,锚点会失效。白屏时间长,需要等待 JavaScript 加载完才渲染 html
  • 动态路由

    • path: '/user/:userId'
    • this.$route.params.userId
路由懒加载
- `const Home = ( ) => import('../components/Home.vue')`
- 路由会定义**很多不同页面**,一般情况下,所有页面打包到**一个 js 文件**中,如果一次性请求的话,会花费不少时间,甚至用户电脑上**出现空白**。这时需要**路由懒加载**来解决
- 将路由对应的组件打包成一个个的 js 代码块,只有这个路由被访问的时候才加载对应组件
- app.js  0.js  1.js  ......
  • 路由传递参数

    • params

      • /user/:id
      • this.$route.params.id
      • this.$router.push('/user/${this.userId}')
    • query

      • /user?id=123
      • this.$route.query.id
      • this.$router.push({ path: '/user', query: { id: this.userId } })
routerrouter 和 route 的区别
- $router
  - $router 为 VueRouter 实例
  - 每个页面的 $router 都一样,全局配置
  - 想要导航到不同 URL 用 $router.push
  
- $route
  - $route 为当前 router 跳转的路由对象
  - 可以获取 name、path、query、params等

- 路由导航守卫

  - 常用钩子函数

    - beforeEach
      - router.beforeEach( (to, from, next) => { next() })
      - to -> 将要跳转的 route 对象
      - from -> 将要离开的 route 对象
      - next -> 调用该方法后,才能进入下一个钩子
    - afterEach

  - 导航守卫(全局守卫)

    - router.beforeEach(路由进入)
      - to, from, next
    - router.beforeResolve(路由解析前)
      - to, from, next
    - router.afterEach(路由离开)
      - to, from,唯一一个路由守卫没有 next

  - 路由独享守卫

    - beforeEnter
      - 在路由配置上直接定义

  - 组件内的守卫

    - beforeRouteEnter
      - 守卫 不能 访问 this,因为守卫在导航确认前被调用,因此即将登场的新组件还没被创建。要传一个回调给 next 来访问组件实例
    - beforeRouteUpdate 
      - this 已经可用了
    - beforeRouteLeave
      - 这个离开守卫通常用来禁止用户在还未保存修改前突然离开。该导航可以通过 next(false) 来取消。

  - 完整的导航解析流程   

    - 在失活的组件里调用 beforeRouteLeave 守卫。
      调用全局的 beforeEach 守卫。
      在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
      在路由配置里调用 beforeEnter。
      解析异步路由组件。
      在被激活的组件里调用 beforeRouteEnter。
      调用全局的 beforeResolve 守卫 (2.5+)。
      导航被确认。
      调用全局的 afterEach 钩子。
      触发 DOM 更新。
      调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。
      
    - beforeRouteLeave  -> beforeEach -> beforeRouteUpdate
    
    - beforeRouteLeave  -> beforeEach -> beforeEnter -> beforeRouterEnter -> beforeResolve -> afterEach -> DOM 更新对应组件 -> 调用 beforeRouteEnter 守卫中传给 next 的回调函数
动态添加路由
完整的导航解析流程
  • keep-alive

    • 如果被包在里面,所有路径匹配到的视图组件都会被缓存,不然会被销毁,再次进入要创建新组件
    • include 、exclude
    • activated / deactivated 钩子

5.vuex

// store.js
import Vue from 'vue'
import Vuex from 'vuex'

import getters from './getters'
import mutations from './mutations'
import actions from './actions'

Vue.use(Vuex)

const state =  {
  cartList: []
}

export default new Vuex.Store({
  state: state,
  getters: getters,
  mutations: mutations,
  actions: actions
})

// main.js
new Vue({
  store,
  render: h => h(App)
}).$mount('#app')
  • state
  • getters
  • mutations
  • actions
  • modules