[TOC]

1.Vue基础部分

属性计算 computed

  • 模板内的表达式不应该包含太多的逻辑,对于任何复杂逻辑,都应当使用计算属性
  • computed属性和methods不同的是计算属性是基于它们的依赖进行缓存的
  • computed属性和watch属性,通常更好的想法是使用computed属性而不是命令式的watch回调。虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的watcher。当想要在数据变化响应时,执行异步操作或开销较大的操作,watch更适合
  • 计算属性默认只拥有getter方法,但是可以自定义一个setter方法
computed: {
   fullName: {
       // getter
       get: function () {
           return this.firstName + " " + this.lastName
       },
       // setter
       set: function (newValue) {
           var names = newValue.split(" ")
           this.firstName = names[0]
           this.lastName = names[names.length - 1]
       }
   }
}
//下面语句触发setter方法,firstName和lastName也会被相应更新
vm.fullName = "John Doe"

观察者属性watch

通过watch属性可以手动观察Vue实例上的数据变动,当然也可以调用实例上的vm.$watch达到相同的目的

<div id="watch-example">
  <p>Ask a yes/no question: <input v-model="question"></p>
  <p>{{ answer }}</p>
</div>
<script>
  var watchExampleVM = new Vue({
    el: "#watch-example",
    data: {
      question: "",
      answer: "I cannot give you an answer until you ask a question!"
    },
    watch: {
      // 如果question发生改变,该函数就会运行
      question: function (newQuestion) {
        this.answer = "Waiting for you to stop typing..."
        this.getAnswer()
      }
    },
    methods: {
      // _.debounce是lodash当中限制操作频率的函数
      getAnswer: _.debounce(
        function () {
          if (this.question.indexOf("?") === -1) {
            this.answer = "Questions usually contain a question mark. ;-)"
            return
          }
          this.answer = "Thinking..."
          var vm = this
          axios.get("https://yesno.wtf/api")
            .then(function (response) {
              vm.answer = _.capitalize(response.data.answer)
            })
            .catch(function (error) {
              vm.answer = "Error! Could not reach the API. " + error
            })
        },
        // 这是用户停止输入等待的毫秒数
        500
      )
    }
  })
</script>
  • 使用watch属性的灵活性在于,当监测到数据变化的时候,可以做一些设置中间状态之类的过渡处理

    实例属性和方法

Vue实例暴露了一系列带有前缀$的实例属性与方法

let vm = new Vue();
vm = {
  // Vue实例属性的代理
  $data: "被watch的data对象",
  $props: "当前组件收到的props",
  $el: "Vue实例使用的根DOM元素",
  $options: "当前Vue实例的初始化选项",
  $parent: "父组件Vue对象的实例",
  $root: "根组件Vue对象的实例",
  $children: "当前实例的直接子组件",
  $slots: "访问被slot分发的内容",
  $scopedSlots: "访问scoped slots",
  $refs: "包含所有拥有ref注册的子组件",
  $isServer: "判断Vue实例是否运行于服务器",
  $attrs: "包含父作用域中非props的属性绑定",
  $listeners: "包含了父作用域中的v-on事件监听器",
  // 数据
  $watch: "观察Vue实例变化的表达式、计算属性函数",
  $set: "全局Vue.set的别名",
  $delete: "全局Vue.delete的别名",
  // 事件
  $on: "监听当前实例上的自定义事件,事件可以由vm.$emit触发",
  $once: "监听一个自定义事件,触发一次之后就移除监听器",
  $off: "移除自定义事件监听器",
  $emit: "触发当前实例上的事件",
  // 生命周期
  $mount: "手动地挂载一个没有挂载的Vue实例",
  $forceUpdate: "强制Vue实例重新渲染,仅影响实例本身和插入插槽内容的子组件",
  $nextTick: "将回调延迟到下次DOM更新循环之后执行",
  $destroy: "完全销毁一个实例",
}

$refs属性

子组件指定ref属性之后,可以通过父组件的$refs实例属性对其进行访问

<div id="parent">
  <user-profile ref="profile"></user-profile>
</div>
<script>
    var parent = new Vue({ el: "#parent" })
    var child = parent.$refs.profile // 访问子组件
</script>
  • $refs会在组件渲染完毕后填充,是非响应式的,仅作为需要直接访问子组件的应急方案,因此要避免在模板或计算属性中使用$refs。

    组件

组件可以扩展HTML元素功能,并且封装可重用代码。可以通过Vue.component( id, [definition] )注册或者获取全局组件

// 注册组件,传入一个扩展过的构造器
Vue.component("my-component", Vue.extend({ ... }))
// 注册组件,传入一个option对象(会自动调用Vue.extend)
Vue.component("my-component", { ... })
// 获取注册的组件(始终返回构造器)
var MyComponent = Vue.component("my-component")

组件的生命周期

  • 不要在Vue实例的属性和回调上使用箭头函数,比如created: () => console.log(this.a)或vm.$watch(“a”, newValue => this.myMethod())。因为箭头函数的this与父级上下文绑定,并不指向Vue实例本身,所以前面代码中的this.a或this.myMethod将会是undefined
  • 通过jQuery对DOM进行的操作可以放置在Mounted属性上进行,即当Vue组件已经完成在DOM上挂载的时候

    2.Vue-Router

    router-link和router-view 组件

    router-link组件: 其实是封装了一个a 标签(链接标签),里面有一个重要的属性to,它指定的一个路径,跳转的路径.
    router-view组件:视图渲染组件,通过router-link跳转到的页面,加载的组件,都会在router-view组件里显示(渲 染)出来。
    需要注意的几点:

    • 路径要对应要渲染的组件在路由列表里定义
    • 一个路由列表,是一个数组.里面包含着路由对象
    • 一个基本的路由对象,包含两个属性: path and component
    • path: 在url 里输入的路径 ;component : 这个路径对应的要渲染的组件

      路由配置

      a.动态路由匹配

  • 首先,创建一个路由对象.

    {
      path: '/user/:name',
    component: () => import('@/views/user.vue')
    }
    
  • 那么,这个name 就是一个动态路由参数

  • 在views 里创建 user.vue ```javascript

    
    - 改变url , 对应的 $route.params.name ,这个动态的,路由的参数,也会改变
    - $route : 代表当前加载页面的路由对象,它是一个对象,里面包含一个params属性,params对象里面又包含了一个name 的值,它是一个动态的值(它是一个形参类型的值,随url 的改变而改变).
    - 只需要传递不同的参数,加载同一个页面,加载不同的逻辑.
    <a name="bRNeU"></a>
    #### b.嵌套路由
    
    - 多层嵌套组件: 可以通过嵌套路由,来渲染.
    - 在路由列表中添加parent路由对象
    ```javascript
    {
        path: '/parent',
      component: () => import('@/views/Parent.vue'),
        children: [
          {
              path: 'child',
            component: () => import('@/views/Child.vue')
          }
        ]
    }
    

    注意:

    在设置路由的时候,我们需要明确 视图——路由的对应关系

    3.Vuex

    Vuex是专门为Vue应用程序提供的状态管理模式,每个Vuex应用的核心是store(仓库),即装载应用程序state(状态)的容器,每个应用通常只拥有一个store实例。
    Vuex的state是响应式的,即store中的state发生变化时,相应组件也会进行更新,修改store当中state的唯一途径是提交mutations。

    const store = new Vuex.Store({
      state: {
        count: 0
      },
      mutations: {
        increment (state) {
          state.count++
        }
      }
    })
    store.commit("increment")       // 通过store.state来获取状态对象
    console.log(store.state.count)  // 通过store.commit()改变状态
    

    State

    从store当中获取state的最简单办法是在计算属性中返回指定的state,每当state发生改变的时候都会重新执行计算属性,并且更新关联的DOM

    const Counter = {
      template: `<div>{{ count }}</div>`,
      computed: {
        count () {
          return store.state.count
        }
      }
    }
    
    • Vuex提供store选项,将state从根组件注入到每个子组件中,从而避免频繁import store。
    // 父组件中注册store属性
    const app = new Vue({
      el: "#app",
      store: store,
      components: { Counter },
      template: `
        <div class="app">
          <counter></counter>
        </div>`
    })
    // 子组件,store会注入到子组件,子组件可通过this.$store进行访问
    const Counter = {
      template: `<div>{{ count }}</div>`,
      computed: {
        count () {
          return this.$store.state.count
        }
      }
    }
    
    • Vuex提供mapState()辅助函数,避免使用多个state的场景下,多次去声明计算属性
    // 在单独构建的版本中辅助函数为 Vuex.mapState
    import { mapState } from "vuex"
    export default {
      computed: mapState({
        count: state => state.count,
        // 传递字符串参数"count"等同于`state => state.count`
        countAlias: "count",
        countPlusLocalState (state) {
          return state.count + this.localCount
        }
      })
    }
    // 当计算属性名称与state子节点名称相同时,可以向mapState传递一个字符串数组
    computed: mapState([
      "count" // 映射this.count到store.state.count
    ])
    
    • mapState()函数返回一个包含有state相关计算属性的对象,这里可以通过ES6的对象展开运算符…将该对象与Vue组件本身的computed属性进行合并
    computed: {
      localComputed () {},
      ...mapState({})
    }
    
    • Vuex允许在store中定义getters(可视为store的计算属性),getters的返回值会根据其依赖被缓存,只有当依赖值发生了改变才会被重新计算。该方法接收state作为第1个参数,其它getters作为第2个参数。可以直接在store上调用getters来获取指定的计算值
    const store = new Vuex.Store({
      state: {
        todos: [
          { id: 1, text: "...", done: true },
          { id: 2, text: "...", done: false }
        ]
      },
      getters: {
        doneTodos: (state, getters) => {
          return state.todos.filter(todo => todo.done)
        }
      }
    })
    // 获取doneTodos = [{ id: 1, text: "...", done: true }]
    store.getters.doneTodos
    

    这样就可以方便的根据store中现有的state派生出新的state,从而避免在多个组件中复用时造成代码冗余

    computed: {
      doneTodosCount () {
        return this.$store.getters.doneTodos // 现在可以方便的在Vue组件使用store中定义的doneTodos
      }
    }
    
    • Vuex提供的mapGetters()辅助函数将store中的getters映射到局部计算属性
    import { mapGetters } from "vuex"
    export default {
      computed: {
        // 使用对象展开运算符将getters混入computed计算属性
        ...mapGetters([
          "doneTodosCount",
          doneCount: "doneTodosCount" // 映射store.getters.doneTodosCount到别名this.doneCount
        ])
      }
    }
    

    Mutations

    修改store中的state的唯一方法是提交mutations,mutations类似于自定义事件,拥有一个字符串事件类型和一个回调函数(接收state作为参数,是对state进行修改的位置)

    const store = new Vuex.Store({
      state: {
        count: 1
      },
      mutations: {
        // 触发类型为increment的mutation时被调用
        increment (state) {
          state.count++ // 变更状态
        }
      }
    })
    // 触发mutation
    store.commit("increment")
    
    • 可以通过store的commit()方法触发指定的mutations,也可以通过store.commit()向mutation传递参数
    // commit()
    store.commit({
      type: "increment",
      amount: 10
    })
    // store
    mutations: {
      increment (state, payload) {
        state.count += payload.amount
      }
    }
    

    mutation事件类型建议使用常量,并且将这些常量放置在单独文件,便于管理和防止重复

    // mutation-types.js
    export const SOME_MUTATION = "SOME_MUTATION"
    // store.js
    import Vuex from "vuex"
    import { SOME_MUTATION } from "./mutation-types"
    const store = new Vuex.Store({
      state: { ... },
      mutations: {
        // 可以通过ES6的计算属性命名特性去使用常量作为函数名
        [SOME_MUTATION] (state) {
          // mutate state
        }
      }
    })
    <!--mutation()必须是同步函数,因为devtool无法追踪回调函数中对state进行的异步修改-->
    <!---->
    
    • Vue组件可以使用this.$store.commit(“xxx”)提交mutation,或者使用mapMutations()将Vue组件中的methods映射为store.commit调用(需要在根节点注入store)
    import { mapMutations } from "vuex"
    export default {
      methods: {
        ...mapMutations([
          "increment" // 映射this.increment()为this.$store.commit("increment")
        ]),
        ...mapMutations({
          add: "increment" // 映射this.add()为this.$store.commit("increment")
        })
      }
    }
    

    Actions

    Action用来提交mutation,且Action中可以包含异步操作。Action函数接受一个与store实例具有相同方法和属性的context对象,因此可以通过调用context.commit提交一个mutation,或者通过context.state和context.getters来获取state、getters

    const store = new Vuex.Store({
      state: {
        count: 0
      },
      mutations: {
        increment (state) {
          state.count++
        }
      },
      actions: {
        increment (context) {
          context.commit("increment")
        }
      }
    })
    
    • 组件中可以使用this.$store.dispatch(“xxx”)分发action,或者使用mapActions()将组件的methods映射为store.dispatch(需要在根节点注入store)
    import { mapActions } from "vuex"
    export default {
      methods: {
        ...mapActions([
          "increment"       // 映射this.increment()为this.$store.dispatch("increment")
        ]),
        ...mapActions({
          add: "increment"  // 映射this.add()为this.$store.dispatch("increment")
        })
      }
    }
    

    store.dispatch可以处理action回调函数当中返回的Promise,并且store.dispatch本身仍然返回一个Promise

    actions: {
      // 定义一个返回Promise对象的actionA
      actionA ({ commit }) {
        return new Promise((resolve, reject) => {
          setTimeout(() => {
            commit("someMutation") // 触发mutation
            resolve()
          }, 1000)
        })
      },
      // 也可以在actionB中分发actionA
      actionB ({ dispatch, commit }) {
        return dispatch("actionA").then(() => {
          commit("someOtherMutation") // 触发另外一个mutation
        })
      }
    }
    // 现在可以分发actionA
    store.dispatch("actionA").then(() => {
      ... ... ...
    })
    

    可以体验通过ES7的异步处理特性async/await来组合action

    actions: {
      async actionA ({ commit }) {
        commit("gotData", await getData())
      },
      async actionB ({ dispatch, commit }) {
        await dispatch("actionA") //等待actionA完成
        commit("gotOtherData", await getOtherData())
      }
    }
    

    Module

    整个应用使用单一状态树的情况下,所有state都会集中到一个store对象,因此store可能变得非常臃肿。因此,Vuex允许将store切割成模块(module),每个模块拥有自己的state、mutation、action、getter、甚至是嵌套的子模块

    const moduleA = {
      state: {},
      mutations: {},
      actions: {},
      getters: {}
    }
    const moduleB = {
      state: {},
      mutations: {},
      actions: {}
    }
    const store = new Vuex.Store({
      modules: {
        a: moduleA,
        b: moduleB
      }
    })
    store.state.a // moduleA的状态
    store.state.b // moduleB的状态
    
    • module内部的mutations()和getters()接收的第1个参数是模块的局部状态对象
    const moduleA = {
      state: { count: 0 },
      mutations: {
        increment (state) {
          state.count++ // 这里的state是模块的局部状态
        }
      },
      getters: {
        doubleCount (state) {
          return state.count * 2
        }
      }
    }
    
    • 模块内部action当中,可以通过context.state获取局部状态,以及context.rootState获取全局状态
    const moduleA = {
      // ...
      actions: {
        incrementIfOddOnRootSum ({ state, commit, rootState }) {
          if ((state.count + rootState.count) % 2 === 1) {
            commit("increment")
          }
        }
      }
    }
    
    • 模块内部的getters()方法,可以通过其第3个参数接收到全局状态
    const moduleA = {
      getters: {
        sumWithRootCount (state, getters, rootState) {
          return state.count + rootState.count
        }
      }
    }
    

    4.构建打包与部署

    打包、部署

    部署就是把代码放到各种不同的环境下运行,因为我们在本地,测试环境,生产环境所需要的资源跟条件都不一样,比如你的是mac系统,他的是windows系统,可是都要运行该程序,那么需要通过 yarn build 或者 npm run build 来将代码进行整理、编译之后得到的dist文件放在同一个服务器上面,让不同的用户进行访问。

    webSever

    搭建webserver的方式有很多种:

    • nodejs 搭建
    • nginx
    • apache
    • iis

      apache与nginx的优缺点比较

      1、nginx相对于apache的优点:
      轻量级,同样起web 服务,比apache 占用更少的内存及资源
      抗并发,nginx 处理请求是异步非阻塞的,而apache 则是阻塞型的,在高并发下nginx 能保持低资源低消耗高性能
      2、最核心的区别在于apache是同步多进程模型,一个连接对应一个进程;
      nginx是异步的,多个连接(万级别)可以对应一个进程

      5.暗号:构建打包与部署