Vue.js 实战经验小结

3,048 阅读4分钟
原文链接: github.com

Vue.js介绍

Vue.js(读音 /vjuː/, 类似于 view) 是一套构建用户界面的 渐进式框架。与其他重量级框架不同的是,Vue 采用自底向上增量开发的设计。Vue 的核心库只关注视图层,并且非常容易学习,非常容易与其它库或已有项目整合。另一方面,Vue 完全有能力驱动采用 单文件组件和  Vue 生态系统支持的库开发的复杂单页应用。

Vue.js 的目标是通过尽可能简单的 API 实现响应的数据绑定组合的视图组件

一、技术选型——为什么要使用Vue.js?

  • 需求:实现一个通过选择、输入等方式创建项目,以及对项目进行管理、查看、编辑等操作的应用。应用中多处需要数据双向绑定
  • 体验:单页应用(Single page application, SPA)用户体验好、快,接近原生应用的体验
  • 效率:提供常用逻辑的封装实现,编程效率高
  • 扩展:允许异步加载组件,并允许直接操作DOM(React需通过refs访问到特定DOM)
  • 轻量:轻量级框架不带有侵略性API,对容器也没有依赖性,易于进行配置,易于通用,启动时间较短。允许我们同时使用习惯的jQuery.js/Zepto.js

二、项目构建——Vue单文件组件刮来的清新风

  • 目录结构:

    {
        "src":{
            "app":{
                "index":["index.html", "index.js", "index.less"]
            },
            "common":[]//js,image等
            "components":{
                "common":["cover.vue", "loading.vue", "toast.vue"],//通用组件
                "home":["index.vue", "tab.vue", "list.vue"],//页面组件
                  //...
                  //单文件组件同HTML文件一样,支持文件内写HTML、CSS、JS
                  //但首页入口需要是常规的html、js
            }
        }
    }
  • 组件化:

    • 组件与模块的关系

      来源:Jasin Yip的回答@知乎

    • 组件化的粒度:本项目以文件夹划分页面,以UI层级及重用关系划分组件

  • 小结

    vue单文件组件将一个组件的结构、样式、逻辑集中在单个类HTML文件里,这在以前是需要通过服务端做拼接处理的,vue可以做到通过js处理,也可以通过打包工具的特定模块处理。

    这样的内容集中降低了工程项目的碎片化,在编程过程中也可以避免写一个组件需要在html、css、js不同文件之间进行频繁切换。

三、代码结构——Vue的使用概览

  • 使用入门

    1. 创建根实例

      index.html

      <div id="app"></div>

      index.js

      var app = new Vue().$mount('#app');
    2. 创建单文件组件

      demo.vue

      <template>
          <div>{{greeting}} world!</div>
      </template>
      <style>
      
      </style>
      <script>
          module.exports = {
              data: function(){
                  return {
                      greeting: 'Hello'
                  }
              }
          }
      </script>
    3. 挂载组件

      index.js

      var demo = require('demo.vue')//Webpack用require,ES6用import
      var app = new Vue({
          components: {
              demo: demo
          }
      }).$mount('#app');

      index.html

      <div id="app">
          <demo></demo>
      </div>
    4. 运行结果

      <div id="app">
          <div>Hello world!</div>
      </div>
  • 实践中遇到的难点

    • 数据双向绑定及监听变化:

      • 数据双向绑定

        双向数据绑定指的就是,绑定对象属性的改变到用户界面的变化的能力,反之亦然

        双向:数据->视图、视图->数据

        绑定:一个内容发生改变,另一个内容随之改变

        demo.vue:一个典型的数据双向绑定示例

        <template>
            <input type="text" v-bind:value="msg" v-on:input="changeMsg($event)"/>
            <div>{{msg}}</div>
        </template>
        <script>
            module.exports = {
                data: function(){
                    'msg':'hello'
                },
                methods: {
                    changeMsg: function(e){
                        this.msg = e.target.value
                    }
                }
            }
        </script>

        Vue提供了v-model指令,对上面的代码简写如下

        demo.vuev-model指令示例

        <template>
            <input type="text" v-model="msg"/>
            <div>{{msg}}</div>
        </template>
        <script>
            module.exports = {
                data: function(){
                    'msg':'hello'
                },
            }
        </script>

        目前为止,要在数据变化后执行逻辑,可以扩展changeMsg内容,或监听inputchange事件。Vue提供了computed、watch两种方式来监听变化

      • computed

        demo.vuecomputed示例

        <template>
            <input type="text" v-model="msg"/>
            <div>{{msg}}</div>
            <div>{{rs}}</div>
        </template>
        <script>
            module.exports = {
                 computed: {
                      rs: function () {
                          console.log(this.msg)
                          return this.msg.split('').reverse().join('')
                      }
                  }
            }
        </script>
      • watch

        demo.vuewatch示例

        <template>
            <input type="text" v-model="msg"/>
            <div>{{msg}}</div>
            <div>{{rs}}</div>
        </template>
        <script>
            module.exports = {
                 data:function () {
                     return {
                         msg: 'hello',
                         rs: 'olleh'
                     }
                 },
                 watch: {
                     msg: function (val) {
                         console.log(val)
                         this.rs = val.split('').reverse().join('')
                     }
                 },
            }
        </script>
      • computed与watch的异同

        1. 相同

          受现代 Javascript 的限制(以及废弃 Object.observe),Vue 不能检测到对象属性的添加或删除

          它们都只能观察到Vue实例对象(Vue实例中,this指向的对象)上存在的属性变化

        2. 不同

          • computed会自动给Vue实例对象上添加属性、覆盖已有属性,watch不会

          • computed中的方法只在被访问时执行,而不是在监听属性变化发生时立即执行

            watch中的方法,在监听属性变化发生时立即执行

            遇到 computed 监听无效,而 watch 有效,原因往往出在这里

          • computed只关心计算结果,不限制多少个属性发生了变化,可以监听多个属性变化的结果

            计算属性的结果会被缓存,除非依赖的响应式属性变化才会重新计算。

            watch只关心某个属性变化,发生变化则执行相应逻辑

    • 单向数据流

      • Vue只封装了从父组件到子组件的通信方式:prop

        props中包含父组件传入的属性、方法,这些内容会被添加到Vue组件的实例对象上

      • Vue建议的从子组件到父组件的通信方式:自定义事件

        父组件提供一个自定义事件,用于接收子组件的信息,执行相应的函数

      • Vue补充的任意组件之间的通信方式:全局自定义事件(非父子组件通信

        1. 设置一个新的Vue实例:

          var bus = new Vue()
        2. *建议将其挂在window上以供工程中全局使用window.Bus = bus

        3. 在组件A中监听自定义事件

          A.vue

          <script>
              module.exports = {
                   created: function(){
                      bus.$on('event-A', function (something) {
                        // ...
                      })
                   }
              }
          </script>
        4. 在组件B中触发自定义事件

          B.vue

          <script>
              module.exports = {
                   created: function(){
                      bus.$emit('event-A', 'say something')
                   }
              }
          </script>
    • 生命周期

      • 基本图示

      • 加入keep-alive后的生命周期

        <template>
            <div id="parent">
                <keep-alive>
                    <component v-bind:is="nowChild"></component>
                </keep-alive>
            </div>
        </template>
        <script>
            var child1 = require('child1.vue')
            var child2 = require('child2.vue')
            module.exports = {
                data: function(){
                    return {
                        nowChild: 'child1'
                    }
                },
                components:{
                    child1: child1,
                    child2: child2
                }
            }
        </script>

        keep-alive的子组件的基本生命周期同keep-alive的父组件相似,但keep-alive的子组件间切换,会触发activateddeactivated,而不会二次触发子组件的基本生命周期

四、体验优化——单页应用的用户体验

  • 原则一:避免外链跳转

    单页应用在单页内有一致的、流畅的用户体验。而单页与外链间的跳转,则会产生大量的计算、渲染,反而暴露出单页应用不“轻量”的缺点。因此,避免外链跳转,降低单页内存在的外链被点击的可能,有助于减少用户对明显体验差异的感知,一定程度上提高用户体验。

    存在需要直接打开外链的情况,优化措施可以是在新窗口中打开。

  • 原则二:优化首屏

    上述“单页应用不 ‘轻量’ ”的问题,在首屏表现尤其明显。进入一个单页,往往是长时间的等待,情况类似于打开一个APP,先看到的一般是首屏图片、动画、引导、广告。

    这里的优化也需要从技术、视图两方面入手。技术上采用服务端渲染、异步加载组件等方案,视图上则需要做到首屏尽快从一片空白变成有点东西。

  • 原则三:使用缓存

    单页应用可以充分实践MV*的理念,视图的更新反映了数据的更新。时间花销主要在于DOM渲染、逻辑运算、等待请求返回中,这些数据均可以被缓存以留到下次使用。

    使用缓存是单页应用体验趋近原生应用的主要因素。