美文网首页
Vue 学习笔记

Vue 学习笔记

作者: 等酒香醇V | 来源:发表于2017-11-28 16:32 被阅读0次

    项目脚手架

    在这里我使用vue-cli来进行安装

    # 全局安装 vue-cli
    $ npm install --global vue-cli
    # 创建一个基于 webpack 模板的新项目
    $ vue init webpack VueDemo
    # 安装依赖,运行
    $ cd VueDemo
    $ npm install
    $ npm run dev
    

    查看项目结构,index.html/main.js是项目入口,App.vue是项目的根组件,通过router/index.js路由控制来进行路由切换。

    vue-1.png

    vue实例

    每个 Vue 应用都是通过 Vue 函数创建一个新的 Vue 实例开始的。详细可以查看API。Vue Api

    new Vue({
      el: '#app',
      router,
      template: '<App/>',
      components: { App }
    })
    

    Vue实例的生命周期如下:

    lifecycle.png

    vue 组件

    vue 组件有两种书写方法。一种是使用vue实例来注册,另一种是单文件组件。我们项目里面使用的是单文件组件的方式,以.vue后缀的文件就是vue组件。

    • 通过vue实例来注册
    <div id="app">
        <runoob></runoob>
    </div>
     
    <script>
    // 注册
    Vue.component('runoob', {
      template: '<h1>自定义组件!</h1>'
    })
    // 创建根实例
    new Vue({
      el: '#app'
    })
    </script>
    
    • 单文件组件。

    文件包含template、script、style三部分。template是需要渲染到HTML里面的模版。script为组件的脚本,在此处定义组件。style为css样式。

    当一个组件被定义,data 必须声明为返回一个初始数据对象的函数,因为组件可能被用来创建多个实例。如果 data 仍然是一个纯粹的对象,则所有的实例将共享引用同一个数据对象!通过提供 data 函数,每次创建一个新实例后,我们能够调用 data 函数,从而返回初始数据的一个全新副本数据对象

    注意,不应该对 data 属性使用箭头函数 (例如data: () => { return { a: this.myProp }})。理由是箭头函数绑定了父级作用域的上下文,所以 this 将不会按照期望指向 Vue 实例,this.myProp 将是 undefined。

    <template>
      <div class="hello">
        <h1>{{ msg }}</h1>
      </div>
    </template>
    
    <script>
    export default {
      name: 'HelloWorld',
      data () {
        return {
          msg: '插值测试'
        }
      }
    }
    </script>
    
    <!-- Add "scoped" attribute to limit CSS to this component only -->
    <style scoped>
    h1, h2 {
      font-weight: normal;
    }
    ul {
      list-style-type: none;
      padding: 0;
    }
    li {
      display: inline-block;
      margin: 0 10px;
    }
    a {
      color: #42b983;
    }
    </style>
    
    

    组件的使用和通讯

    在vue文件中使用其他组件的方式如下,组件的标签名称必须跟引入时的名称一致,不区分大小写。

    <template>
      <Test msg="asdf"></Test>
    </template>
    
    <script>
    import Test from '@/components/Test'
    
    export default {
      name: 'HelloWorld',
      components: {
        Test
      }
    }
    </script>
    

    父组件向子组件传递值时可以使用Prop 传递数据。例如上面例子里面的往Test组件传递msg的值,子组件实现如下:

    <template>
      <span>测试页面:{{ msg }}</span>
    </template>
    
    <script>
    export default {
      name: 'Test',
      props: ['msg']
    }
    </script>
    

    如果需要绑定动态的值则使用v-bind来实现<Test :msg="msg"></Test>

    子组件给父组件传递数据。使用emit来传递。v-on可以缩写成@

    <button v-on:click="chirdSay()">子组件按钮</button>
    
      methods: {
        chirdSay: function () {
          this.$emit('chirdSay', this.msg)
        }
      }
      
      <Test :msg="array[0]" v-on:chirdSay="chirdSay"></Test>
      
        chirdSay: function (data) {
          alert(data.name)
        }
    

    组件插槽

    使用slot可以将父组件的内容插入子组件。

    <!-- 子组件 -->
    <slot name="testSlot"></slot>
    
    <!-- 父组件 -->
      <Test :msg="array[0]" v-on:chirdSay="chirdSay">
        <p slot="testSlot">testSlot</p>
      </Test>
    

    数据绑定

    vue可以使用{{data}}的方式来进行数据渲染,这种用法其实是v-text指令简写<h2 v-text="msg"></h2>。类似的有v-html,绑定html格式文件。v-bind:href,绑定标签属性,<input type="text" v-bind:value="msg"/>,v-bind:value可以缩写成:value。

    如果需要进行双向数据绑定的时候,这个时候我们需要使用的是v-model。<input type="text" v-model="msg"/>

    事件绑定

    vue使用v-on指令来进行事件绑定,在这里我们测试一下click事件,v-on:click可以缩写成@click。代码如下

    <button v-on:click="say('hi')">Say Hi</button>
    
    ......
    
      methods: {
        say: function (data) {
          alert(data)
        }
      }
    

    循环、判断

    判断是否显示使用v-if/v-show,v-if是通过删除增加dom元素,v-show是改变css的display属性来进行显示隐藏。v-if还可以跟v-else一块使用。

    <p v-if="show">显示</p>
    <p v-else>不显示</p>
    

    循环使用v-for指令来实现。

        <ui v-for="item in array" v-bind:key="item.id">
          <li>{{item.name}}</li>
        </ui>
    

    遍历对象属性,v-for可以通过下面的方式遍历对象。同时v-for也提供了(data,key,index)的方式来遍历,第一个参数为属性值,第二个为属性名称,第三个参数为索引。

    <ui v-for="item in array" :key="item.id">
          <li v-for="(o, key, index) in item">{{key}}:{{o}}:{{index}}</li>
    </ui>
    

    跟v-if配合使用

    <li v-for="todo in todos" v-if="!todo.isComplete">
      {{ todo }}
    </li>
    

    Class/style绑定

    使用v-bind:class/style进行样式绑定。

    <p v-bind:class="{active: active}" v-on:click="activeRed()">绑定class测试,点击激活红色</p>
    
    
    activeRed: function () {
      this.active = true
    }
    
    .active {
      color: red;
    }    
    

    多个判断条件

    <div class="static"
         v-bind:class="{ active: isActive, 'text-danger': hasError }">
    </div>
    

    也可以直接绑定数值对象,下面例子跟上面等价

    <div v-bind:class="classObject"></div>
    
    data: {
      classObject: {
        active: true,
        'text-danger': false
      }
    }
    

    其他用法

    <div v-bind:class="[isActive ? activeClass : '', errorClass]"></div>
    <div v-bind:class="[{ active: isActive }, errorClass]"></div>
    <div :class="[{active: item.classifyId == activeId}]"></div>
    

    绑定内联样式

    <div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
    
    
    <div v-bind:style="styleObject"></div>
    
    data: {
      styleObject: {
        color: 'red',
        fontSize: '13px'
      }
    }
    

    数组更新检测

    Vue 包含一组观察数组的变异方法,所以它们也将会触发视图更新。这些方法如下:

    • push()
    • pop()
    • shift()
    • unshift()
    • splice()
    • sort()
    • reverse()

    注意:由于 JavaScript 的限制,Vue 不能检测以下变动的数组:

    • 当你利用索引直接设置一个项时,例如:vm.items[indexOfItem] = newValue

    解决方法:

    // Vue.set
    Vue.set(example1.items, indexOfItem, newValue)
    // Array.prototype.splice
    example1.items.splice(indexOfItem, 1, newValue)
    
    • 当你修改数组的长度时,例如:vm.items.length = newLength

    解决方法:example1.items.splice(newLength)

    对象更改检测

    还是由于 JavaScript 的限制,Vue 不能检测对象属性的添加或删除,例如:

    var vm = new Vue({
      data: {
        a: 1
      }
    })
    // `vm.a` 现在是响应式的
    vm.b = 2
    // `vm.b` 不是响应式的
    

    解决方法:

    你可以添加一个新的 age 属性到嵌套的 userProfile 对象:
    Vue.set(vm.userProfile, 'age', 27)
    你还可以使用 vm.$set 实例方法,它只是全局 Vue.set 的别名:
    this.$set(this.userProfile, 'age', 27)
    

    动态多选框

    <select v-model="selected">
      <option v-for="option in options" v-bind:value="option.value">
        {{ option.text }}
      </option>
    </select>
    <span>Selected: {{ selected }}</span>
    
    new Vue({
      el: '...',
      data: {
        selected: 'A',
        options: [
          { text: 'One', value: 'A' },
          { text: 'Two', value: 'B' },
          { text: 'Three', value: 'C' }
        ]
      }
    })
    

    自定义指令

    在js里面定义指令

    // 注册一个全局自定义指令 `v-focus`
    Vue.directive('focus', {
      // 当被绑定的元素插入到 DOM 中时……
      inserted: function (el) {
        // 聚焦元素
        el.focus()
      }
    })
    

    在组件中使用指令

    <input v-focus>
    
    directives: {
      focus: {
        // 指令的定义
        inserted: function (el) {
          el.focus()
        }
      }
    }
    

    指令的钩子函数、参数及更高级用法参考:指令使用

    vue2+vuex+axios全家桶集成

    vue中一般使用vuex来进行数据共享,axios来获取数据。

    安装vuexnpm install vuex --save-dev

    在项目的入口js文件main.js中:

    import Vue from 'vue'
    import Vuex from 'vuex'
    import App from './App'
    import router from './router'
    import store from './store/index'
    
    Vue.config.productionTip = false
    Vue.use(Vuex)
    
    /* eslint-disable no-new */
    new Vue({
      el: '#app',
      router,
      store,
      template: '<App/>',
      components: { App }
    })
    

    新建一个/store/index.js

    import Vue from 'vue'
    import Vuex from 'vuex'
    
    Vue.use(Vuex)
    
    const store = new Vuex.Store({
      // 定义状态
      state: {
        author: 'xxx'
      },
      mutations: {
        changeAuthor: function (state, newValue) {
            state.author = newValue
        } 
      }
    })
    
    export default store
    

    在组件中使用

    {{$store.state.author}}
    xx = this.$store.state.author
    this.$store.state.author = xx
    this.$store.commit('changeAuthor','xx');
    

    安装axios npm install axios --save-dev

    axios使用,这里只使用了get方法,其他的可以查看axios使用说明

    import axios from 'axios'
    
    axios.get('static/data.json').then((res) => {
        this.$store.commit('changeAuthor',res.data.author);
    });
    
    
    

    路由

    vue-cli 已经集成了vue-router,在这里就直接按照官方的步骤来实践一下。

    在router/index.js里面配置好路由信息。

    import Vue from 'vue'
    import Router from 'vue-router'
    import HelloWorld from '@/components/HelloWorld'
    import Dashboard from '@/components/Dashboard'
    import Home from '@/components/Home'
    
    Vue.use(Router)
    
    export default new Router({
      routes: [
        {
          path: '/',
          name: 'HelloWorld',
          component: HelloWorld
        }, {
          path: '/home',
          name: 'Home',
          component: Home
        }, {
          path: '/dashboard',
          name: 'Dashboard',
          component: Dashboard
        }
      ]
    })
    

    在main.js中引入路由,在App.vue中加入<router-view/>便可以在这个位置显示路由相对应的组件。

    通过<router-link to="/home">go to home</router-link>可以进行路由的跳转。

    动态路由

    类似于angularjs,vue-router也可以通过如下方式来实现动态路由。path: '/user/:id', component: User,此时id将被绑定在this.$route.params中,在组件中通过this.$route.params.id来获取值。

    在组件中使用$route会使之与其对应路由形成高度耦合,从而使组件只能在某些特定的url上使用,限制了其灵活性。

    使用props将组件和路由解耦:

    // 路由中定义,增加props属性并设为true。
    { path: '/user/:id', component: User, props: true }
    
    // 在组件中定义及使用,
    props: ['id'],
    template: '<div>User {{ id }}</div>'
    
    

    嵌套路由

    vue中嵌套路由需要使用children属性来实现,嵌套路由的用法如下:

    {
          path: '/hello',
          name: 'HelloWorld',
          component: HelloWorld,
          children: [
            {
              path: 'test',
              component: Test
            }
          ]
        }
    

    在HelloWorld中需要添加<router-view/>,要注意,以 / 开头的嵌套路径会被当作根路径。

    编程式导航

    除了在html中通过<router-link to>来进行路由跳转之外,在javascript中,我们还可以使用this.$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' }})
    
    //注意:如果提供了 path,params 会被忽略,上述例子中的 query 并不属于这种情况。取而代之的是下面例子的做法,你需要提供路由的 name 或手写完整的带有参数的 path:
    
    const userId = 123
    router.push({ name: 'user', params: { userId }}) // -> /user/123
    router.push({ path: `/user/${userId}` }) // -> /user/123
    // 这里的 params 不生效
    router.push({ path: '/user', params: { userId }}) // -> /user
    

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

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

    参数传递

    除了上面通过路径传递的方式之外,还可以使用query来进行参数传递,对比如下:

    //路径的方式
     {
          path: '/home/:searchKey',
          name: 'Home',
          component: Home
        }
    <router-link :to="{name: 'Home', params:{searchKey:'better'}}">
    this.$router.push({name:'Home' ,params:{searchKey:'better'}});
    console.log(this.$route.params.searchKey);
    
    // 结果 .../home/better
    
    //参数的方式
    {
          path: '/home',
          name: 'Home',
          component: Home
        }
    <router-link :to="{name: 'Home',query:{searchKey:'better'}}">
    this.$router.push({name:'Home' ,query:{searchKey:'better'}});
    console.log(query:this.$route.query.searchKey);
    
    // 结果  .../home?searchKey=better
    
    

    路由重定向和别名

    例子:

        {
          path: '/dashboard',
          name: 'Dashboard',
          component: Dashboard,
          alias: '/bb'
        }, {
          path: '/aa',
          redirect: '/dashboard'
        }
    

    访问/aa将跳转到/dashboard,访问/bb等价于/dashboard。vue使用redirect进行重定向,使用alias来设置别名。

    路由守卫

    全局守卫,注意需要增加next(),才会进行下面其他的路由守卫。

    router.beforeEach((to, from, next) => {
      console.log('全局路由守护');
      // next('/hello');
      next();
    })
    

    每个守卫方法接收三个参数:

    • to: Route: 即将要进入的目标 路由对象

    • from: Route: 当前导航正要离开的路由

    • next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。

    • next(): 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。

    • next(false): 中断当前的导航。如果浏览器的 URL 改变了(可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。

    • next('/') 或者 next({ path: '/' }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。

    • next(error): (2.4.0+) 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError() 注册过的回调。

    在单个路由里面配置路由守卫,参数同上。

        {
          path: '/hello',
          name: 'HelloWorld',
          component: HelloWorld,
          beforeEnter: (to, from, next) => {
            console.log('hello 路由守护');
          },
          children: [
            {
              path: 'test',
              component: Test
            }
          ]
        }
    

    vue 钩子测试及各种配置用法

    <template>
        <div class="content">
        </div>
    </template>
    
    <script>
        export default {
            name: 'Shop',
            data() {
                return {
                    a: 1
                }
            },
            computed: {
                //计算属性
                // 仅读取
                aDouble: function() {
                    return this.a * 2
                },
                // 读取和设置
                aPlus: {
                    get: function() {
                        return this.a + 1
                    },
                    set: function(v) {
                        this.a = v - 1
                    }
                }
            },
            methods: {
                chirdSay: function() {
                    alert("hello");
                }
            },
            watch: {
                a: function(val, oldVal) {
                    console.log('new: %s, old: %s', val, oldVal)
                }
            },
            beforeCreate() {
                //组件创建前调用
                console.log("beforeCreate");
            },
            created() {
                //组件创建完成后调用,才能调用组件的属性,方法。
                this.aPlus = 33;
                console.log(this.aPlus);
                console.log("created");
            },
            beforeMount() {
                console.log("beforeMount");
            },
            mounted() {
                console.log("mounted");
            },
            beforeUpdate() {
                console.log("beforeUpdate");
            },
            updated() {
                console.log("updated");
            },
            activated() {
                console.log("activated");
            },
            deactivated() {
                console.log("deactivated");
            },
            beforeDestroy() {
                console.log("beforeDestroy");
            },
            beforeRouteEnter(to, from, next) {
                // 在渲染该组件的对应路由被 confirm 前调用
                // 不!能!获取组件实例 `this`
                // 因为当守卫执行前,组件实例还没被创建
                console.log("beforeRouteEnter");
                next(vm => {
                    vm.$store.state.title = '点餐平台';
                });
            },
            beforeRouteUpdate(to, from, next) {
                // 在当前路由改变,但是该组件被复用时调用
                // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
                // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
                // 可以访问组件实例 `this`
                console.log("beforeRouteUpdate");
                next();
            },
            beforeRouteLeave(to, from, next) {
                // 导航离开该组件的对应路由时调用
                // 可以访问组件实例 `this`
                console.log("beforeRouteLeave");
                next();
            }
        }
    </script>
    
    <style>
        .el-icon-star-on {
            font-size: 4px;
            margin-right: 0px;
        }
    </style>
    

    组件实现调用beforeRouteEnter,通过next(vm = > {}) 在组件mounted完成后可以访问组件实例,然后调用beforeCreate,此时组件还未初始化,不能调用组件的属性。接着调用created,初始化组件完毕,可以使用组件实例。接着调用beforeMount》mounted进行挂载前和挂载的逻辑,挂载不太明白,掠过。之后next(vm = > {})里面的语句倍调用。离开时调用beforeRouteLeave》beforeDestroy。

    其他

    1. $nextTick,页面元素加载完后调用。有时候使用v-for等动态加载html的时候,获取对应元素会出现undefined,此时使用$nextTick可以在加载完后再进行取值。
    this.$nextTick(function() {
    
    })
    
    1. $refs,获取dom元素。
    //html
    <div class="scroll" ref="list">
    //javascript
    console.log(this.$refs.list);
    
    //滚动条操作。
    this.$refs.list.clientHeight  //获取元素高度
    
    this.$refs.list.addEventListener('scroll', function(){}); //监听滚动事件
    
    this.$refs.list.scrollTo(0, height); //使滚动条滚动到对应位置。
    
    this.$refs.list.scrollTop; //当前滚动条位置
    

    注:基本上是参考官方手册进行学习,如有不明白可以参考官方手册

    相关文章

      网友评论

          本文标题:Vue 学习笔记

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