vue-study

1、基础知识

2、生态系统

vue + vue-router + vuex

  • 工具

    Devtools

    Vue CLI

  • 核心插件:

vue router

vuex

vue 服务端渲染

2.1 vue-router

Vue Router 是 Vue.js 官方的路由管理器。它和 Vue.js 的核心深度集成,让构建单页面应用变得易如反掌。包含的功能有:

  • 嵌套的路由/视图表
  • 模块化的、基于组件的路由配置
  • 路由参数、查询、通配符
  • 基于 Vue.js 过渡系统的视图过渡效果
  • 细粒度的导航控制
  • 带有自动激活的 CSS class 的链接
  • HTML5 历史模式或 hash 模式,在 IE9 中自动降级
  • 自定义的滚动条行为

router.push(location, onComplete?, onAbort?)

注意:在 Vue 实例内部,你可以通过$router 访问路由实例。因此你可以调用 this.$router.push

想要导航到不同的 URL,则使用  router.push 方法。这个方法会向 history 栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,则回到之前的 URL。

当你点击  时,这个方法会在内部调用,所以说,点击  等同于调用 router.push(...)

声明式 编程式
router.push(...)
// 字符串
router.push('home')

// 对象
router.push({ path: 'home' })

// 命名的路由
router.push({ name: 'user', params: { userId: '123' }})

// 带查询参数,变成 /register?plan=private
router.push({ path: 'register', query: { plan: 'private' }})
//
router.push({ path: `/user/${userId}` }) // -> /user/123

注意: 如果目的地和当前路由相同,只有参数发生了改变 (比如从一个用户资料到另一个 /users/1 -> /users/2),你需要使用 beforeRouteUpdate 来响应这个变化 (比如抓取用户信息)。

router.replace(location, onComplete?, onAbort?)

跟 router.push 很像,唯一的不同就是,它不会向 history 添加新记录,而是跟它的方法名一样 —— 替换掉当前的 history 记录。

声明式 编程式
router.replace(...)

#router.go(n)

这个方法的参数是一个整数,意思是在 history 记录中向前或者后退多少步,类似 window.history.go(n)

完整的导航解析流程

  1. 导航被触发。
  2. 在失活的组件里调用离开守卫。
  3. 调用全局的 beforeEach 守卫。
  4. 在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
  5. 在路由配置里调用 beforeEnter
  6. 解析异步路由组件。
  7. 在被激活的组件里调用 beforeRouteEnter
  8. 调用全局的 beforeResolve 守卫 (2.5+)。
  9. 导航被确认。
  10. 调用全局的 afterEach 钩子。
  11. 触发 DOM 更新。
  12. 用创建好的实例调用 beforeRouteEnter 守卫中传给 next 的回调函数。

2.2 vuex

状态管理:

数据可以简单地分为两个部分,跨组件的数据组件内的数据

vuex可以说是专门为vue设计的状态管理工具。和 Redux 中使用不可变数据来表示 state 不同,Vuex 中没有 reducer 来生成全新的 state 来替换旧的 state**,Vuex 中的 state 是可以被修改的**。这么做的原因和 Vue 的运行机制有关系,Vue 基于 ES5 中的 getter/setter 来实现视图和数据的双向绑定,因此 Vuex 中 state 的变更可以通过 setter 通知到视图中对应的指令来实现视图更新。
Vuex 中的 state 是可修改的,而修改 state 的方式不是通过 actions,而是通过 mutations。更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。
vuex的数据流简单地说为

  • 在视图中触发 action,并根据实际情况传入需要的参数
  • 在 action 中触发所需的 mutation,在 mutation 函数中改变 state
  • 通过 getter/setter 实现的双向绑定会自动更新对应的视图

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

1.核心概念

state——存储、数据
mutation——修改数据、追踪;同步
action——封装:组合;异步

Getter-- 可以认为是 store 的计算属性,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。

Module-模块

2.辅助方法

vuex辅助方法:
mapState state -> computed
mapActions actions -> methods
mapGetters getters -> computed

3.Mutation 必须是同步函数

一条重要的原则就是要记住 mutation 必须是同步函数

因为:

mutation 如果是异步函数:因为当 mutation 触发的时候,回调函数还没有被调用,devtools 不知道什么时候回调函数实际上被调用——实质上任何在回调函数中进行的状态的改变都是不可追踪的。

import { mapMutations } from 'vuex'

export default {
  // ...
  methods: {
    ...mapMutations([
      'increment', // 将 `this.increment()` 映射为 `this.$store.commit('increment')`

      // `mapMutations` 也支持载荷:
      'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.commit('incrementBy', amount)`
    ]),
    ...mapMutations({
      add: 'increment' // 将 `this.add()` 映射为 `this.$store.commit('increment')`
    })
  }
}

4.Getter:

通过属性访问

this.$store.getters.doneTodosCount

注意,getter 在通过属性访问时是作为 Vue 的响应式系统的一部分缓存其中的。

#通过方法访问

store.getters.getTodoById(2) // -> { id: 2, text: '...', done: false }

注意,getter 在通过方法访问时,每次都会去进行调用,而不会缓存结果。

mapGetters 辅助函数
mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性:

import { mapGetters } from 'vuex'

export default {

  // ...

  computed: {

  // 使用对象展开运算符将 getter 混入 computed 对象中
...mapGetters([
  'doneTodosCount',
  'anotherGetter',
  // ...
])}

}

严格模式

在严格模式下,无论何时发生了状态变更且不是由 mutation 函数引起的,将会抛出错误。这能保证所有的状态变更都能被调试工具跟踪到

const store = new Vuex.Store({
  // ...
  strict: process.env.NODE_ENV !== 'production'
})

不要在发布环境下启用严格模式!严格模式会深度监测状态树来检测不合规的状态变更——请确保在发布环境下关闭严格模式,以避免性能损失。

4.表单处理

当在严格模式中使用 Vuex 时,在属于 Vuex 的 state 上使用 v-model 会比较棘手:


解决方法1

给  中绑定 value,然后侦听 input 或者 change事件,在事件回调中调用 action:



// ...
computed: {
  ...mapState({
    message: state => state.obj.message
  })
},
methods: {
  updateMessage (e) {
    this.$store.commit('updateMessage', e.target.value)
  }
}

解决方法2:使用带有 setter 的双向绑定计算属性:



// ...
computed: {
  message: {
    get () {
      return this.$store.state.obj.message
    },
    set (value) {
      this.$store.commit('updateMessage', value)
    }
  }
}

3、VUE项目目录结构

├── index.html
├── main.js
├── api
│   └── ... # 抽取出API请求
├── components
│   ├── App.vue
│   └── ...
└── store
    ├── index.js          # 我们组装模块并导出 store 的地方
    ├── actions.js        # 根级别的 action
    ├── mutations.js      # 根级别的 mutation
    └── modules
        ├── cart.js       # 购物车模块
        └── products.js   # 产品模块

4、生产环境部署

4.1模板预编译

当使用 DOM 内模板或 JavaScript 内的字符串模板时,模板会在运行时被编译为渲染函数。通常情况下这个过程已经足够快了,但对性能敏感的应用还是最好避免这种用法。

预编译模板最简单的方式就是使用单文件组件——相关的构建设置会自动把预编译处理好,所以构建好的代码已经包含了编译出来的渲染函数而不是原始的模板字符串。

5、vue 中 this 指向问题

5.1method 不能使用箭头函数

因为箭头函数绑定了父级作用域的上下文,所以 this 将不会按照期望指向 Vue 实例。

5.2method 中方法使用定时器需要使用箭头函数

箭头函数中的this指向是固定不变的,即是在定义函数时的指向 而普通函数中的this指向时变化的,即是在使用函数时的指向

methods: {
  goPage: function (index) {
    this.newPage = index
  },
  inv: function () {
    this.invt = setInterval(() => {
      this.goPage(this.nextPage)
      console.log(this)
      //此时的this指向是该vue组件,不管在哪使用,指向都是该vue组件
    }, 1000)
  }
}

setInterval() 与 setTimeout() 是 window 对象的函数,所以 this 会指向 window

6、插件 Vue.use()

6.1Vue.use源码

插件的使用方法概括出来就是:

  • 1、通过Vue.use(MyPlugin)使用,本质上是调用MyPlugin.install(Vue)
  • 2、使用插件必须在new Vue()启动应用之前完成,实例化之前就要配置好。
  • 3、如果使用Vue.use多次注册相同插件,那只会注册成功一次。
Vue.use = function (plugin) {   
    // 忽略已注册插件
    if (plugin.installed) {
      return
    }
    
    // 集合转数组,并去除第一个参数
    var args = toArray(arguments, 1);
    
    // 把this(即Vue)添加到数组的第一个参数中
    args.unshift(this);
    
    // 调用install方法
    if (typeof plugin.install === 'function') {
      plugin.install.apply(plugin, args);
    } else if (typeof plugin === 'function') {
      plugin.apply(null, args);
    }
    
    // 注册成功
    plugin.installed = true;
    return this;
  };

6.2自定义插件

按照插件的开发流程,应该有一个公开方法install,在install里面使用全局的mixin方法添加一些组件选项,mixin方法包含一个created钩子函数,在钩子函数中验证this.$options.rules

import Vue from 'vue'

// 定义插件
const RulesPlugin = {

  // 插件应该有一个公开方法install
  // 第一个参数是Vue 构造器
  // 第二个参数是一个可选的选项对象
  install (Vue) {
  
    // 注入组件
    Vue.mixin({
    
      // 钩子函数
      created: function () {
      
        // 验证逻辑
        const rules = this.$options.rules
        if (rules) {
          Object.keys(rules).forEach(key => {
          
            // 取得所有规则
            const { validate, message } = rules[key]
            
            // 监听,键是变量,值是函数
            this.$watch(key, newValue => {
            
              // 验证规则
              const valid = validate(newValue)
              if (!valid) {
                console.log(message)
              }
            })
          })
        }
      }
    })
  }
}

// 调用插件,实际上就是调用插件的install方法

// 即RulesPlugin.install(Vue)

Vue.use(RulesPlugin)

7、实现响应式系统 Object.defineproperty()

1、观察者observe()

  • 1、传入对象obj作为参数
  • 2、使用Object.defineProperty转换对象的所有属性

2、observe()转换对象的属性使之响应式,对于每个转换后的属性,它会被分配一个Dep实例,该实例跟踪订阅update函数列表,并在调用setter时触发它们重新运行

  • 1、创建一个Dep类,包含两个方法:dependnotify
  • 2、创建一个autorun函数,传入一个update函数作为参数
  • 3、在update函数中调用dep.depend(),显式依赖于Dep实例
  • 4、调用dep.notify()触发update函数重新运行

3、autorun()接收update函数作为参数,并在update函数订阅的属性发生变化时重新运行

const state = {
  count: 0
}

observe(state)

autorun(() => {
  console.log(state.count)
})
// 输出 count is: 0

state.count++
// 输出 count is: 1

observe中修改obj属性的同时分配Dep的实例,并在get中注册订阅者,在set中通知改变。autorun函数保存不变。实现如下:

class Dep {

  // 初始化
  constructor () {          
    this.subscribers = new Set()
  }

  // 订阅update函数列表
  depend () {
    if (activeUpdate) {     
      this.subscribers.add(activeUpdate)
    }
  }

  // 所有update函数重新运行
  notify () {              
    this.subscribers.forEach(sub => sub())
  }
}

function observe (obj) {

  // 迭代对象的所有属性
  // 并使用Object.defineProperty()转换成getter/setters
  Object.keys(obj).forEach(key => {
    let internalValue = obj[key]

    // 每个属性分配一个Dep实例
    const dep = new Dep()

    Object.defineProperty(obj, key, {
    
      // getter负责注册订阅者
      get () {
        dep.depend()
        return internalValue
      },

      // setter负责通知改变
      set (newVal) {
        const changed = internalValue !== newVal
        internalValue = newVal
        
        // 触发后重新计算
        if (changed) {
          dep.notify()
        }
      }
    })
  })
  return obj
}

let activeUpdate = null

function autorun (update) {

  // 包裹update函数到"wrappedUpdate"函数中,
  // "wrappedUpdate"函数执行时注册和注销自身
  const wrappedUpdate = () => {
    activeUpdate = wrappedUpdate
    update()
    activeUpdate = null
  }
  wrappedUpdate()
}

vue-study_第1张图片

8、Render函数原理及实现

在初始化阶段,本质上发生在auto run函数中,然后通过render函数生成Virtual DOMview根据Virtual DOM生成Actual DOM。因为render函数依赖于页面上所有的数据data,并且这些数据是响应式的,所有的数据作为组件render函数的依赖。一旦这些数据有所改变,那么render函数会被重新调用。
在更新阶段,render函数会重新调用并且返回一个新的Virtual Dom,新旧Virtual DOM之间会进行比较,把diff之后的最小改动应用到Actual DOM中。

vue-study_第2张图片

8.1JSX和Template

JSX和Template都是用于声明DOM和state之间关系的一种方式,在Vue中,Template是默认推荐的方式,但是也可以使用JSX来做更灵活的事。
JSX更加动态化,对于使用编程语言是很有帮助的,可以做任何事,但是动态化使得编译优化更加复杂和困难。
Template更加静态化并且对于表达式有更多约束,但是可以快速复用已经存在的模板,模板约束意味着可以在编译时做更多的性能优化,相对于JSX在编译时间上有着更多优势

<script type="text/x-template" id="anchored-heading-template">
  <h1 v-if="level === 1">
    <slot></slot>
  </h1>
  <h2 v-else-if="level === 2">
    <slot></slot>
  </h2>
  <h3 v-else-if="level === 3">
    <slot></slot>
  </h3>
  <h4 v-else-if="level === 4">
    <slot></slot>
  </h4>
  <h5 v-else-if="level === 5">
    <slot></slot>
  </h5>
  <h6 v-else-if="level === 6">
    <slot></slot>
  </h6>
script>

渲染函数,它比模板更接近编译器

Vue.component('anchored-heading', {
  render: function (createElement) {
    return createElement(
      'h' + this.level,   // 标签名称
      this.$slots.default // 子节点数组
    )
  },
  props: {
    level: {
      type: Number,
      required: true
    }
  }
})

9、常用指令

9. 1、 v-cloak

可以使用v-cloak指令用于隐藏未编译完成的插值表达式,一般我们在使用时会与添加一个隐藏该元素的样式同时使用。





{{msg}}

9.2、 v-text 与 v-html

v-text 会将数据以字符串文本的形式更新,而 v-html 则是将数据以 html 标签的形式更新。

9.3、 v-bind

v-bind 可以用来在标签上绑定标签的属性(例如:img 的 src、title 属性等等)和样式(可以用style的形式进行内联样式的绑定,也可以通过指定class的形式指定样式)。同时,对于绑定的内容,是做为一个 js 变量,因此,我们可以对该内容进行编写合法的 js 表达式


9.4、 v-on

事件修饰符

  • .stop:阻止事件冒泡
  • .prevent:阻止默认事件
  • .capture:添加事件监听器时使用事件捕获模式
  • .self:只当在 event.target 是当前元素自身时触发处理函数(比如不是子元素冒泡引起的事件触发)
  • .once:事件只触发一次
  • .passive:滚动事件的默认行为 (即滚动行为) 将会立即触发




...
...

使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用 v-on:click.prevent.self 会阻止所有的点击,而 v-on:click.self.prevent 只会阻止对元素自身的点击。

9.5、 v-model

9.6、 v-if 与 v-show

v-if 是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。

v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。

相比之下,v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。

一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。

注意,v-show 不支持 元素,也不支持 v-else。

9.7、 v-for

不推荐同时使用 v-if 和 v-for

当 v-if 与 v-for 一起使用时,v-for 具有比 v-if 更高的优先级

你可能感兴趣的:(vue)