zoukankan      html  css  js  c++  java
  • 前端MVC Vue2学习总结(二)——Vue的实例、生命周期与Vue脚手架(vue-cli)

    一、Vue的实例

    1.1、创建一个 Vue 的实例

    每个 Vue 应用都是通过 Vue 函数创建一个新的 Vue 实例开始的:

    var vm = new Vue({
    // 选项
    })

    虽然没有完全遵循 MVVM 模型,Vue 的设计无疑受到了它的启发。因此在文档中经常会使用 vm (ViewModel 的简称) 这个变量名表示 Vue 实例。

    1、vue.js就是一个构造器,通过构造器Vue来实例化一个对象;例如:var vm = new Vue({});
    2、实例化Vue时,需要传入一个参数(选项对象);
    3、参数:选项对象可以包含,数据(data)、挂载元素(el)、方法(methods)、模版(template)、生命周期函数等等;
    4、扩展构造器Vue,从而用预定义选项创建可复用的组件构造器,所有组件都是被扩展的Vue的实例,使用Vue.extend({})来扩展;
    注意:尽管可以命令式地创建扩展实例,不过在多数情况下建议将组件构造器注册为一个自定义元素,然后声明式地用在模板中。

    当创建一个 Vue 实例时,你可以传入一个选项对象。这篇教程主要描述的就是如何使用这些选项来创建你想要的行为。作为参考,你也可以在 API 文档 中浏览完整的选项列表。
    一个 Vue 应用由一个通过 new Vue 创建的根 Vue 实例,以及可选的嵌套的、可复用的组件树组成。举个例子,一个 todo 应用的组件树可以是这样的:

    Root Instance
    └─ TodoList
    ├─ TodoItem
    │ ├─ DeleteTodoButton
    │ └─ EditTodoButton
    └─ TodoListFooter
    ├─ ClearTodosButton
    └─ TodoListStatistics

    我们会在稍后的组件系统章节具体展开。不过现在,你只需要明白所有的 Vue 组件都是 Vue 实例,并且接受相同的选项对象即可 (一些根实例特有的选项除外)。

    1.2、数据与方法

    当一个 Vue 实例被创建时,它向 Vue 的响应式系统中加入了其 data 对象中能找到的所有的属性。当这些属性的值发生改变时,视图将会产生“响应”,即匹配更新为新的值。

    // 我们的数据对象
    var data = { a: 1 }
    
    // 该对象被加入到一个 Vue 实例中
    var vm = new Vue({
      data: data
    })
    
    // 他们引用相同的对象!
    vm.a === data.a // => true
    
    // 设置属性也会影响到原始数据
    vm.a = 2
    data.a // => 2
    
    // ... 反之亦然
    data.a = 3
    vm.a // => 3

    当这些数据改变时,视图会进行重渲染。值得注意的是只有当实例被创建时 data 中存在的属性是响应式的。也就是说如果你添加一个新的属性,像:

    vm.b = 'hi'

    那么对 b 的改动将不会触发任何视图的更新。

    示例:

    <!DOCTYPE html>
    <html>
    
        <head>
            <meta charset="UTF-8">
            <title>vue2实例</title>
        </head>
    
        <body>
            <div id="app1">
                <input type="text" v-model="a"/>
            </div>
            <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
            <script type="text/javascript">
                var data={a:1}
                //实例
                var vm = new Vue({
                    el: "#app1",
                    data:data,
                    updated:function(){
                        console.log("实例被更新了!");
                    }
                });
            </script>
        </body>
    
    </html>

    结果:

    如果你知道你会在晚些时候需要一个属性,但是一开始它为空或不存在,那么你仅需要设置一些初始值。比如:

    data: {
    newTodoText: '',
    visitCount: 0,
    hideCompletedTodos: false,
    todos: [],
    error: null
    }

    除了 data 属性,Vue 实例暴露了一些有用的实例属性与方法。它们都有前缀 $,以便与用户定义的属性区分开来。例如:

    var data = { a: 1 }
    var vm = new Vue({
    el: '#example',
    data: data
    })
    
    vm.$data === data // => true
    vm.$el === document.getElementById('example') // => true
    
    // $watch 是一个实例方法
    vm.$watch('a', function (newValue, oldValue) {
    // 这个回调将在 `vm.a` 改变后调用
    })

    在未来,你可以在 API 参考查阅到完整的实例属性和方法的列表

    1.3、实例属性

    解释

    vm._uid // 自增的id
    vm._isVue // 标示是vue对象,避免被observe
    vm._renderProxy // Proxy代理对象
    vm._self // 当前vm实例
    
    vm.$parent // 用于自定义子组件中,指向父组件的实例
    vm.$root // 指向根vm实例
    vm.$children // 当前组件的子组件实例数组
    vm.$refs 
    
    vm._watcher = null
    vm._inactive = null
    vm._directInactive = false
    vm._isMounted = false // 标识是否已挂载
    vm._isDestroyed = false // 标识是否已销毁
    vm._isBeingDestroyed = false // 标识是否正在销毁
    
    vm._events // 当前元素上绑定的自定义事件
    vm._hasHookEvent // 标示是否有hook:开头的事件
    
    vm.$vnode // 当前自定义组件在父组件中的vnode,等同于vm.$options._parentVnode
    vm._vnode // 当前组件的vnode
    vm._staticTrees // 当前组件模板内分析出的静态内容的render函数数组
    vm.$el // 当前组件对应的根元素
    
    vm.$slots // 定义在父组件中的slots,是个对象键为name,值为响应的数组
    vm.$scopedSlots = emptyObject
    // 内部render函数使用的创建vnode的方法
    vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)
    // 用户自定义render方法时,传入的参数
    vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)
    
    vm._props // 被observe的存储props数据的对象
    vm._data // 被observe的存储data数据的对象
    vm._computedWatchers // 保存计算属性创建的watcher对象

    1.4、实例方法

    1.5、实例参数vm.$options

    vm.$options其实也就是我们new Vue(options)options这个选项对象可传入的属性

    declare type ComponentOptions = {
      // data
      data: Object | Function | void;  // 传入的data数据
      props?: { [key: string]: PropOptions }; // props传入的数据
      propsData?: ?Object;  // 对于自定义组件,父级通过`props`传过来的数据
      computed?: {  // 传入的计算属性
        [key: string]: Function | {
          get?: Function;
          set?: Function;
          cache?: boolean
        }
      };
      methods?: { [key: string]: Function }; // 传入的方法
      watch?: { [key: string]: Function | string };  // 传入的watch
    
      // DOM
      el?: string | Element;  // 传入的el字符串
      template?: string;  // 传入的模板字符串
      render: (h: () => VNode) => VNode;  // 传入的render函数
      renderError?: (h: () => VNode, err: Error) => VNode;
      staticRenderFns?: Array<() => VNode>;
    
      // 钩子函数
      beforeCreate?: Function;
      created?: Function;
      beforeMount?: Function;
      mounted?: Function;
      beforeUpdate?: Function;
      updated?: Function;
      activated?: Function;
      deactivated?: Function;
      beforeDestroy?: Function;
      destroyed?: Function;
    
      // assets
      directives?: { [key: string]: Object }; // 指令
      components?: { [key: string]: Class<Component> }; // 子组件的定义
      transitions?: { [key: string]: Object };
      filters?: { [key: string]: Function }; // 过滤器
    
      // context
      provide?: { [key: string | Symbol]: any } | () => { [key: string | Symbol]: any };
      inject?: { [key: string]: string | Symbol } | Array<string>;
    
      // component v-model customization
      model?: {
        prop?: string;
        event?: string;
      };
    
      // misc
      parent?: Component; // 父组件实例
      mixins?: Array<Object>; // mixins传入的数据
      name?: string; // 当前的组件名
      extends?: Class<Component> | Object; // extends传入的数据
      delimiters?: [string, string]; // 模板分隔符
    
      // 私有属性,均为内部创建自定义组件的对象时使用
      _isComponent?: true;  // 是否是组件
      _propKeys?: Array<string>; // props传入对象的键数组
      _parentVnode?: VNode; // 当前组件,在父组件中的VNode对象
      _parentListeners?: ?Object; // 当前组件,在父组件上绑定的事件
      _renderChildren?: ?Array<VNode>; // 父组件中定义在当前元素内的子元素的VNode数组(slot)
      _componentTag: ?string;  // 自定义标签名
      _scopeId: ?string;
      _base: Class<Component>; // Vue
      _parentElm: ?Node; // 当前自定义组件的父级dom结点
      _refElm: ?Node; // 当前元素的nextSlibing元素,即当前dom要插入到_parentElm结点下的_refElm前
    }

    1.5.1、computed计算属性

    在模板中绑定表达式是非常便利的,但是它们实际上只用于简单的操作。在模板中放入太多的逻辑会让模板过重且难以维护。例如: 

    <span>{{msg.split('').reverse().join('')}}</span>

    使用计算属性定义成一个方法可以复用且模板会更加简洁:

    <!DOCTYPE html>
    <html>
    
        <head>
            <meta charset="UTF-8">
            <title>vue2实例</title>
        </head>
    
        <body>
            <div id="app1">
                <p>
                    <input type="text" v-model="msg" />
                    <span>{{msg.split('').reverse().join('')}}</span>
                </p>
                <p>
                    <input type="text" v-model="msg" />
                    <span>{{revMsg}}</span>
                </p>
            </div>
            <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
            <script type="text/javascript">
                var app1 = new Vue({
                    el: "#app1",
                    data: {
                        msg: "hello"
                    },
                    computed: {
                        revMsg: function() {
                            return this.msg.split('').reverse().join('');
                        }
                    }
                });
            </script>
        </body>
    
    </html>

    结果:

    注意:

    1、computed中定义的方法只允许当着属性用,不能带参数,这限制它的复用性。

    2、当方法中的属性发生变化时方法将重新调用

    3、不应该使用箭头函数来定义计算属性函数 

    4、 computed计算属性可以对属性进行缓存的,计算属性只有当该属性发生变化的时候才会重新计算值

    5、如果一个属性不能完成需要的功能时可以考虑转成计算

    1.5.2、watch计算属性

    一个对象,键是需要观察的表达式,值是对应回调函数。值也可以是方法名,或者包含选项的对象。Vue 实例将会在实例化时调用 $watch(),遍历 watch 对象的每一个属性。

    示例:

    <!DOCTYPE html>
    <html>
    
        <head>
            <meta charset="UTF-8">
            <title>vue2实例</title>
        </head>
    
        <body>
            <div id="app1">
                <p>
                    a:
                    <input type="text" v-model="a" />{{a}}<br/> b:
                    <input type="text" v-model="b" />{{b}}<br/> c:
                    <input type="text" v-model="c.x.y.z" />{{c.x.y.z}}<br/> d:
                    <input type="text" v-model="d" />{{d}}<br/>
                </p>
                <p>
                    n:<input type="text" v-model="c.x.y.n" />{{c.x.y.n}}
                </p>
            </div>
            <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
            <script type="text/javascript">
                var app1 = new Vue({
                    el: "#app1",
                    data: {
                        a: 1,
                        b: 2,
                        c: {
                            x: {
                                y: {
                                    z: 3,
                                    n: 3
                                }
                            }
                        },
                        d: 4
                    },
                    watch: {
                        a: function(val, oldVal) {
                            console.log('a新: %5s, 原: %5s', val, oldVal);
                        },
                        // 方法名
                        b: 'watchb',
                        //对象,深度监视
                        c: {
                            handler: function(val, oldVal) {
                                console.log('c新: %5s, 原: %5s', JSON.stringify(val),JSON.stringify(oldVal));
                            },
                            deep:true
                        },
                        //立即监视
                        d: {
                            handler: function(val, oldVal) {
                                console.log('c新: %5s, 原: %5s', val,oldVal);
                            },
                            immediate:true  //设置初始值时也将调用
                        }
                    },
                    methods: {
                        watchb: function(val, oldVal) {
                            console.log('b新: %5s, 原: %5s',  val, oldVal);
                        }
                    }
                });
    
                var watchb = function(val, oldVal) {
                    console.log('b新: %5s, 原: %5s', val, oldVal);
                }
            </script>
        </body>
    
    </html>

    结果:

    注意:不应该使用箭头函数来定义 watcher 函数、对象类型时并非深拷贝的,只是引用。

    1.5.3、方法methods

    methods 将被混入到 Vue 实例中。可以直接通过 VM 实例访问这些方法,或者在指令表达式中使用。方法中的 this 自动绑定为 Vue 实例。

    var vm = new Vue({
      data: { a: 1 },
      methods: {
        plus: function () {
          this.a++
        }
      }
    })
    vm.plus()
    vm.a // 2

     示例:

    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="UTF-8">
            <title>vue2实例</title>
        </head>
        <body>
            <div id="app1">
                <button type="button" v-on:click="add(2)">{{msg}}</button>
            </div>
            <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
            <script type="text/javascript">
                var app1 = new Vue({
                    el: "#app1",
                    data:{
                        msg:"vue"
                    },
                    methods:{
                        add:function(str){
                            return this.msg+=str;
                        }
                    }
                });
                console.log(app1.add(3));
            </script>
        </body>
    
    </html>

    结果:

    注意,不应该使用箭头函数来定义 method 函数 (例如 plus: () => this.a++)。理由是箭头函数绑定了父级作用域的上下文,所以 this 将不会按照期望指向 Vue 实例,this.a 将是 undefined。

    1.5.4、小结

    computed是计算属性的,methods是计算方法的,最主要的区别是 computed计算属性可以对
    属性进行缓存的,计算属性只有当该属性发生变化的时候才会重新计算值,只要值没有改变,它是不会重新渲染的,但是methods方法不同,每次调用该方法的时候,都会重新执行的。

    1、每个Vue的实例都会代理其data对象里的所有属性,被代理的属性是响应的;

    2、如果实例创建之后添加新的属性到实例上,不会触发视图更新;

    3、不要在实例属性或者回调函数中(如 vm.$watch('a', newVal => this.myMethod()))使用箭头函数。因为箭头函数绑定父上下文,所以 this 不会像预想的一样是 Vue 实例,而是 this.myMethod 未被定义。

    4、Vue实例暴露了一些有用的实例属性和方法,带有前缀 $ 便于与代理的data区分

    a、vm.$el:类型(HTMLElement)挂载元素,Vue实例的DOM根元素;
    b、vm.$data:类型(Object),Vue实例观察的数据对象
    c、vm.$props:类型(Object),属性
    d、vm.$options:类型(Object),用于当前Vue实例的初始化选项,在选项中需要包含自定义属性的时候很有用。
    e、vm.$parent:类型(Vue实例),父实例。
    f、vm.$root:类型(Vue实例),当前组件树的根Vue实例,如果没有父实例,就是实例本身。
    h、vm.$children:类型(Array(Vue实例)),当前实例的直接子组件
    需要注意 $children 并不保证顺序,也不是响应式的。如果你发现自己正在尝试使用 $children 来进行数据绑定,考虑使用一个数组配合 v-for 来生成子组件,并且使用 Array 作为真正的来源。
    i、vm.$slots:类型({ [name: string]: ?Array<VNode> }),用来访问被 slot 分发的内容。每个具名 slot 有其相应的属性(例如:slot="foo" 中的内容将会在 vm.$slots.foo 中被找到)。default 属性包括了所有没有被包含在具名 slot 中的节点。
    k、vm.$refs:类型(Object),一个对象,其中包含了所有拥有 ref 注册的子组件;
    l、vm.$isServer:类型(boolean),当前Vue实例是否运行于服务器;

    官网对应

    1.5.5、箭头函数

    箭头函数是ES6引入的一种语法糖,使得写函数更加简便,类似Lambda表达式,基本格式如下:

    ()=>{}

    示例:

    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="UTF-8">
            <title>vue2实例</title>
        </head>
        <body>
            <div id="app1">
            </div>
            <script type="text/javascript">
                var m1=a=>a+1;
                console.log(m1(100));
                //类似
                var m2=function(a){
                    return a+1;
                }
                console.log(m2(100));
                
                var m3=(a,b)=>a+b;
                console.log(m3(100,200));
                
                var m4=(a,b)=>{a++; b++; return a+b;};  //如果方法体中有多个表达式,则需要大括号与return
                console.log(m4(100,200));
            </script>
        </body>
    
    </html>

    结果:

    二、生命周期

    2.1、实例生命周期

    每个 Vue 实例在被创建之前都要经过一系列的初始化过程。例如需要设置数据监听、编译模板、挂载实例到 DOM、在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做生命周期钩子的函数,给予用户机会在一些特定的场景下添加他们自己的代码。
    比如 created 钩子可以用来在一个实例被创建之后执行代码:

    new Vue({
    data: {
    a: 1
    },
    created: function () {
    // `this` 指向 vm 实例
    console.log('a is: ' + this.a)
    }
    })
    // => "a is: 1"


    也有一些其它的钩子,在实例生命周期的不同场景下调用,如 mounted、updated、destroyed。钩子的 this 指向调用它的 Vue 实例。
    不要在选项属性或回调上使用箭头函数,比如 created: () => console.log(this.a) 或 vm.$watch('a', newValue => this.myMethod())。因为箭头函数是和父级上下文绑定在一起的,this 不会是如你所预期的 Vue 实例,经常导致 Uncaught TypeError: Cannot read property of undefined 或 Uncaught TypeError: this.myMethod is not a function 之类的错误。

    2.2、生命周期图示

    下图说明了实例的生命周期。你不需要立马弄明白所有的东西,不过随着你的不断学习和使用,它的参考价值会越来越高。

    中文版:

    1. beforeCreate

    在实例初始化之后,数据观测(data observer) 和 event/watcher 事件配置之前被调用。

    2. created

    实例已经创建完成之后被调用。在这一步,实例已完成以下的配置:数据观测(data observer),属性和方法的运算, watch/event 事件回调。然而,挂载阶段还没开始,$el 属性目前不可见。 可以在组件的这个期间请求数据,如果是keep-alive组件会被缓存起来,生命周期不会再次触发,如果需要更新数据可以watch当前router变化,如果router是当前组件所在的router则请求数据。

    methods : {
    getData : function(id){
    ...
    this.content = 'test';
    }
    },
    created : function(){
    this.getData(this.id);
    }
    ...
    watch : {
    $route : function(){
    if(this.$route.name == 'xxx'){
    this.getData(this.id);
    }
    }
    }

    3. beforeMount

    在挂载开始之前被调用:相关的 render 函数首次被调用。

    4. mounted

    vm.$el已挂载在文档内,对已有dom节点的操作可以在这期间进行。

    5. beforeUpdate

    数据更新时调用,发生在虚拟 DOM 重新渲染和打补丁之前。

    可以在这个钩子中进一步地更改状态,这不会触发附加的重渲染过程。

    6.updated

    由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。

    当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。然而在大多数情况下,你应该避免在此期间更改状态,因为这可能会导致更新无限循环。

    7.activated

    keep-alive 组件激活时调用。

    8.deactivated

    keep-alive 组件停用时调用。

    9.beforeDestroy

    实例销毁之前调用。在这一步,实例仍然完全可用。

    10.destroyed

    Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。

    2.2.0、控制台输出技巧

    console.log支持的格式标志有:

    示例:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <script>
        var tom={name:"tom",age:10};
        console.group("开始");
        console.group("第一组");
        console.log("%c%s%o","background:red;color:yellow;",'对象是:',tom);
        console.groupEnd();
    
        console.group("第二组");
        console.log("%c%s%o","background:red;color:yellow;",'对象是:',tom);
        console.groupEnd();
    
        console.group("第三组");
        console.log("%c%s%o","background:red;color:yellow;",'对象是:',tom);
        console.groupEnd();
        console.groupEnd();
    </script>
    </body>
    </html>

    结果:

    2.3、生命周期示例一

    示例1:

    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="UTF-8">
            <title>vue2生命周期</title>
        </head>
        <body>
            <div id="app1">
                <input v-model="msg" /> {{msg}}
            </div>
            <button type="button" onclick="destroy()">销毁</button>
            <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
            <script type="text/javascript">
                //格式化输出
                console.log("示例:%c%s","background:red;color:#fff","vue2生命周期","开始了");
                
                var app1 = new Vue({
                    el: "#app1",
                    data:{
                        msg:"vue"
                    },
                    beforeCreate:function(){
                        console.log("创建前:"+this.msg);
                    },
                    created:function(){
                        console.log("创建后:"+this.msg+","+this.$el);
                    },
                    beforeMount:function(){
                        console.log("挂载前:");
                        console.log(this.$el);
                    },
                    mounted:function(){
                        console.log("挂载后:");
                        console.log(this.$el);
                    },
                    beforeUpdate:function(){
                        console.log("实例更新前:");
                        console.log(this.msg);
                        console.log(this.$el);
                    },
                    updated:function(){
                        console.log("实例更新后:");
                        console.log(this.msg);
                        console.log(this.$el);
                    },
                    beforeDestroy:function(){
                        console.log("实例销毁前:");
                        console.log(this.msg);                                        
                    },
                    destroyed:function(){
                        console.log("实例销毁后:");
                        console.log(this.msg);
                    }
                });
                
                function destroy(){
                    app1.$destroy();
                }
            </script>
        </body>
    
    </html>

    初始化结果1:

    修改msg的值为vue2后的结果:

    执行销毁:

    2.4、生命周期示例二

    示例2:

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8">
        <title>Vue2 Demo</title>
    </head>
    <body>
    <div id="app01">
        <h2>{{name}}</h2>
        <button @click="changename">修改name</button>
        <button @click="over">销毁实例</button>
    </div>
    <script src="../../js/vue/vue.min.js"></script>
    <script>
        var vm = new Vue({
            data: {
                name: 'zhangguo',
                bar:"test"
            },
            el: "#app01",
            methods: {
                changename: function () {
                    this.name += ',Hello';
                },
                over:function () {
                    //销毁实例
                    this.$destroy();
                }
            },
            beforeCreate: function () {
                console.group('1、beforeCreate 创建前状态===============》');
                console.log("%c%s", "color:red", "el     : " + this.$el); //undefined
                console.log("%c%o", "color:red", "data   : " + this.$data); //undefined
                console.log("%c%s", "color:red", "name: " + this.name); //undefined
                console.groupEnd()
            },
            created: function () {
                console.group('2、created 创建后状态===============》');
                console.log("%c%s", "color:red", "el     : " + this.$el);
                console.log("%c%s", "color:red", "data   : " + JSON.stringify(this.$data));
                console.log("%c%s", "color:red", "name: " + this.name);
                console.groupEnd()
            },
            beforeMount: function () {
                console.group('3、beforeMount 挂载前===============》');
                console.log("%c%s", "color:red", "el     : ", this.$el);
                console.log("%c%s", "color:red", "data   : " + JSON.stringify(this.$data));
                console.log("%c%s", "color:red", "name: " + this.name);
                console.groupEnd()
            },
            mounted: function () {
                console.group('4、mounted 挂载后===============》');
                console.log("%c%s", "color:red", "el     : ", this.$el);
                console.log("%c%s", "color:red", "data   : " + JSON.stringify(this.$data));
                console.log("%c%s", "color:red", "name: " + this.name);
                console.groupEnd()
            },
            beforeUpdate: function () {
                console.group('5、beforeUpdate 视图更新前===============》');
                console.log("%c%s", "color:red", "el     : ", this.$el.innerHTML);
                console.log("%c%s", "color:red", "data   : " + JSON.stringify(this.$data));
                console.log("%c%s", "color:red", "name: " + this.name);
                console.groupEnd()
            },
            updated: function () {
                console.group('6、updated 视图更新后===============》');
                console.log("%c%s", "color:red", "el     : ", this.$el.innerHTML);
                console.log("%c%s", "color:red", "data   : " + JSON.stringify(this.$data));
                console.log("%c%s", "color:red", "name: " + this.name);
                console.groupEnd()
            },
            beforeDestroy: function () {
                console.group('7、beforeDestroy 实例销毁前===============》');
                console.log("%c%s", "color:red", "el     : ", this.$el.innerHTML);
                console.log("%c%s", "color:red", "data   : " + JSON.stringify(this.$data));
                console.log("%c%s", "color:red", "name: " + this.name);
                console.groupEnd()
            }
            ,
            destroyed: function () {
                console.group('8、beforeDestroy 实例销毁后===============》');
                console.log("%c%s", "color:red", "el     : ", this.$el.innerHTML);
                console.log("%c%s", "color:red", "data   : " + JSON.stringify(this.$data));
                console.log("%c%s", "color:red", "name: " + this.name);
                console.groupEnd()
            }
        });
    </script>
    </body>
    </html>

    初始化结果:

    更新name的值:

    销毁实例

    2.5、手动挂载与调用事件

    2.5.1、手动挂载

    vm.$mount( [elementOrSelector] ) 如果 Vue 实例在实例化时没有收到 el 选项,则它处于“未挂载”状态,没有关联的 DOM 元素。可以使用 vm.$mount() 手动地挂载一个未挂载的实例。

    如果没有提供 elementOrSelector 参数,模板将被渲染为文档之外的的元素,并且你必须使用原生 DOM API 把它插入文档中。
    这个方法返回实例自身,因而可以链式调用其它实例方法。

    var MyComponent = Vue.extend({
    template: '<div>Hello!</div>'
    })
    
    // 创建并挂载到 #app (会替换 #app)
    new MyComponent().$mount('#app')
    
    // 同上
    new MyComponent({ el: '#app' })
    
    // 或者,在文档之外渲染并且随后挂载
    var component = new MyComponent().$mount()
    document.getElementById('app').appendChild(component.$el)

    示例:

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8">
        <title>Vue2 Demo</title>
    </head>
    <body>
    <div id="app01">
    </div>
    <button type="button" onclick="mount01()">手动挂载方法1</button>
    <button type="button" onclick="mount02()">手动挂载方法2</button>
    <button type="button" onclick="mount03()">手动挂载方法3</button>
    <script src="../../js/vue/vue.min.js"></script>
    <script>
        var vm = new Vue({
            data: {
                name: 'zhangguo'
            },
            template:'<h2>{{name}}</h2>'
        });
    
        function mount01() {
            //方法1
            //手动挂载到指定DOM
            vm.$mount("#app01");
        }
    
        function mount02() {
            //方法1
            //手动挂载,触发编译
            vm.$mount();
            //将编译生成的内容元素添加到要挂载的DOM中,作为子元素
            document.getElementById("app01").appendChild(vm.$el);
        }
    
        function mount03() {
            //扩展出一个新的Vue构造器
            var Component01=Vue.extend({
                template:"<h2>{{name}}</h2>"
            });
    
            var c01=new Component01({
                data: {
                    name: 'zhangguo'
                },
                el:"#app01"
            });
        }
    
    </script>
    </body>
    </html>

    运行结果:

    2.5.2、销毁实例

    vm.$destroy() 完全销毁一个实例。清理它与其它实例的连接,解绑它的全部指令及事件监听器。

    2.5.3、强制更新

    vm.$forceUpdate() 迫使 Vue 实例重新渲染。注意它仅仅影响实例本身和插入插槽内容的子组件,而不是所有子组件。

    三、Vue脚手架(vue-cli)

    单页Web应用(single page web application,SPA),就是只有一张Web页面的应用,是加载单个HTML 页面并在用户与应用程序交互时动态更新该页面的Web应用程序。

    提供一个官方命令行工具,可用于快速搭建大型单页应用(SPA)。该工具为现代化的前端开发工作流提供了开箱即用的构建配置。只需几分钟即可创建并启动一个带热重载、保存时静态检查以及可用于生产环境的构建配置的项目:

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

    注意:CLI 工具假定用户对 Node.js 和相关构建工具有一定程度的了解。如果你是新手,我们强烈建议先在不用构建工具的情况下通读指南,在熟悉 Vue 本身之后再使用 CLI。

    3.1、环境搭建

    3.1.1、安装node.js

    从node.js官网下载并安装node,安装过程很简单,一路“下一步”就可以了。安装完成之后,打开命令行工具(win+r,然后输入cmd),输入 node -v,如下图,如果出现相应的版本号,则说明安装成功。

    如果安装不成功,可以直接把安装包修改成压缩包,解压后配置环境变量也可以,就成了绿色版。

     

    这里需要说明下,因为在官网下载安装node.js后,就已经自带npm(包管理工具)了,另需要注意的是npm的版本最好是3.x.x以上,以免对后续产生影响。

    注意版本不能太低,如果您已经安装了低版本的node可以使用npm直接更新。

    3.1.2、修改npm为淘宝镜像

    因为npm的仓库有许多在国外,访问的速度较慢,建议修改成cnpm,换成taobao的镜像。

    打开命令行工具,复制如下配置:

    npm install -g cnpm --registry=https://registry.npm.taobao.org

    安装这里是因为我们用的npm的服务器是外国,有的时候我们安装“依赖”的时候很很慢很慢超级慢,所以就用这个cnpm来安装我们说需要的“依赖”。安装完成之后输入 cnpm -v,如下图,如果出现相应的版本号,则说明安装成功。

    版本号:

    3.1.3、安装webpack

    安装webpack,打开命令行工具输入:

    npm install webpack -g

    安装完成之后输入

    webpack -v

    如下图,如果出现相应的版本号,则说明安装成功。

    3.1.4、安装vue-cli脚手架构建工具

    打开命令行工具输入:

    cnpm install vue-cli -g

    安装完成之后输入 vue -V(注意这里是大写的“V”),如下图,如果出现相应的版本号,则说明安装成功。

    3.2、构建项目

    1)、在硬盘上找一个文件夹放工程用的。这里有两种方式指定到相关目录:

    ①cd 目录路径

    ②如果以安装git的,在相关目录右键选择Git Bash Here

    2)、安装vue脚手架输入:vue init webpack projectName,注意这里的“projectName” 是项目的名称可以说是随便的起名,但是“不能用中文”。

    提示选择项:

    $ vue init webpack exprice --------------------- 这个是那个安装vue脚手架的命令
    This will install Vue 2.x version of the template. ---------------------这里说明将要创建一个vue 2.x版本的项目
    For Vue 1.x use: vue init webpack#1.0 exprice
    ? Project name (exprice) ---------------------项目名称
    ? Project name exprice
    ? Project description (A Vue.js project) ---------------------项目描述
    ? Project description A Vue.js project
    ? Author Datura --------------------- 项目创建者
    ? Author Datura
    ? Vue build (Use arrow keys)
    ? Vue build standalone
    ? Install vue-router? (Y/n) --------------------- 是否安装Vue路由,也就是以后是spa(但页面应用需要的模块)
    ? Install vue-router? Yes
    ? Use ESLint to lint your code? (Y/n) n ---------------------是否启用eslint检测规则,这里个人建议选no
    ? Use ESLint to lint your code? No
    ? Setup unit tests with Karma + Mocha? (Y/n)
    ? Setup unit tests with Karma + Mocha? Yes
    ? Setup e2e tests with Nightwatch? (Y/n)
    ? Setup e2e tests with Nightwatch? Yes
    vue-cli · Generated "exprice".
    To get started: --------------------- 这里说明如何启动这个服务
    cd exprice
    npm install
    npm run dev
    
    View Code

    3)、cd 命令进入创建的工程目录,首先cd projectName;

    4)、安装项目依赖:npm install,因为自动构建过程中已存在package.json文件,所以这里直接安装依赖就行。不要从国内镜像cnpm安装(会导致后面缺了很多依赖库),但是但是如果真的安装“个把”小时也没成功那就用:cnpm install 吧

    5)、安装 vue 路由模块 vue-router 和网络请求模块 vue-resource,输入:cnpm install vue-router vue-resource --save。

    目录:

    |-- build                            // 项目构建(webpack)相关代码
    |   |-- build.js                     // 生产环境构建代码
    |   |-- check-version.js             // 检查node、npm等版本
    |   |-- dev-client.js                // 热重载相关
    |   |-- dev-server.js                // 构建本地服务器
    |   |-- utils.js                     // 构建工具相关
    |   |-- webpack.base.conf.js         // webpack基础配置
    |   |-- webpack.dev.conf.js          // webpack开发环境配置
    |   |-- webpack.prod.conf.js         // webpack生产环境配置
    |-- config                           // 项目开发环境配置
    |   |-- dev.env.js                   // 开发环境变量
    |   |-- index.js                     // 项目一些配置变量
    |   |-- prod.env.js                  // 生产环境变量
    |   |-- test.env.js                  // 测试环境变量
    |-- src                              // 源码目录
    |   |-- components                     // vue公共组件
    |   |-- store                          // vuex的状态管理
    |   |-- App.vue                        // 页面入口文件
    |   |-- main.js                        // 程序入口文件,加载各种公共组件
    |-- static                           // 静态文件,比如一些图片,json数据等
    |   |-- data                           // 群聊分析得到的数据用于数据可视化
    |-- .babelrc                         // ES6语法编译配置
    |-- .editorconfig                    // 定义代码格式
    |-- .gitignore                       // git上传需要忽略的文件格式
    |-- README.md                        // 项目说明
    |-- favicon.ico 
    |-- index.html                       // 入口页面
    |-- package.json                     // 项目基本信息

    3.3、运行项目

    6)、启动项目,输入:npm run dev。服务启动成功后浏览器会默认打开一个“欢迎页面”,如下图:

    编译成功后可以直接在浏览器中查看项目:

    3.4、使用WebStorm IDE创建Vue-cli项目

    在webstorm中新建项目,选择vue-cli:

    输出项目名称:

    项目描述:

    作者:

    构建模式:

    是否使用路由:

    是否使用ESLint作语法检查:

    是否单元测试:

    是否安装e2e(测试):

    是否使用npm作为包管理器:

    选择安装包:

    如果网速比较慢,在上面这个界面中不要选择用npm,手动在控制台cnmp install

    所有的包下载完成后就可以构建 并运行了:

    运行结果:

    3.5、Vue-cli HelloWorld

    了解了默认的模板内容,我们可以开始定义自己的vue程序了,这里写一个简单的HelloWorld,在src目录下创建一个Hi.vue文件,内容如下:

    <template>
        <div id="app1">
            <input v-model="msg" v-on:click="sayhi"/>
            <p>
                <h2>{{msg}}</h2>
            </p>
        </div>
    </template>
    
    <script>
        export default {
            name: 'Hi',
            data() {
                return {
                    msg: 'My First vue-cli app!'
                }
            },
            methods:{
                sayhi:function(){
                    alert(this.msg);
                }
            }
        }
    </script>
    
    <style>
        #app1 {
            font-family: "microsoft yahei";
            color: dodgerblue;
            font-size: 20px;
        }
    </style>

    修改main.js

    // The Vue build version to load with the `import` command
    // (runtime-only or standalone) has been set in webpack.base.conf with an alias.
    import Vue from 'vue'
    import App from './Hi'
    
    Vue.config.productionTip = false
    
    /* eslint-disable no-new */
    new Vue({
      el: '#app',
      template: '<App/>',
      components: { App }
    })

    运行结果:

    3.6、Vue-cli路由与单页

    3.6.1、创建一个带路由的项目

    创建好的项目如下

    3.6.2、定义组件

    在src/compontents目录下创建3个vue组件

    CompontentA:

    <template>
      <div>
        <h2>{{msg}}</h2>
      </div>
    </template>
    
    <script>
      export default {
        name: "ComponentA",
        data(){
          return {
            msg:"ComponentA,这是组件A!",
          }
        }
      }
    </script>
    
    <style scoped>
      h2 {
        color: crimson;
      }
    </style>

    ComponentB:

    <template>
      <div>
        <h2>{{msg}}</h2>
      </div>
    </template>
    
    <script>
      export default {
        name: "ComponentB",
        data(){
          return {
            msg:"ComponentB,这是组件B!",
          }
        }
      }
    </script>
    
    <style scoped>
      h2 {
        color:limegreen;
      }
    </style>

    ComponentC:

    <template>
      <div>
        <h2>{{msg}}</h2>
      </div>
    </template>
    
    <script>
      export default {
        name: "ComponentC",
        data(){
          return {
            msg:"ComponentC,这是组件C!",
          }
        }
      }
    </script>
    
    <style scoped>
      h2 {
        color:dodgerblue;
      }
    </style>

    结果:

    3.6.3、修改路由

    修改src/router目录下的index.js文件

    增加路由,一个组件对应一个访问地址:

    import Vue from 'vue'
    import Router from 'vue-router'
    import App from '@/App'
    import A from '@/components/ComponentA'
    import B from '@/components/ComponentB'
    import C from '@/components/ComponentC'
    
    Vue.use(Router);
    
    export default new Router({
      routes: [
        {
          path: '/',
          name: 'App',
          component: App
        }, {
          path: '/a',
          name: 'ComponentA',
          component: A
        }, {
          path: '/b',
          name: 'ComponentB',
          component: B
        }, {
          path: '/c',
          name: 'ComponentC',
          component: C
        }
      ]
    }) 

    3.6.4、引用链接地址

    修改index.html文件:

    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width,initial-scale=1.0">
        <title>vuecli06</title>
      </head>
      <body>
        <div id="app">
          <ul>
            <li><a href="/">首页</a> </li>
            <li><a href="#/a">组件A</a> </li>
            <li><a href="#/b">组件B</a> </li>
            <li><a href="#/c">组件C</a> </li>
          </ul>
          <!--router-view主要是构建 SPA (单页应用) 时,方便渲染你指定路由对应的组件。你可以 router-view 当做是一个容器,它渲染的组件是你使用 vue-router 指定的。-->
          <router-view></router-view>
        </div>
        <!-- built files will be auto injected -->
      </body>
    </html>

    router-view主要是构建 SPA (单页应用) 时,方便渲染你指定路由对应的组件。你可以 router-view 当做是一个容器,它渲染的组件是你使用 vue-router 指定的。

    打包后运行结果:

    点击a

    点击b

    点击c

    3.7、Vue-cli中依赖jQuery与BootStrap

    3.7.1、添加依赖包

    项目根目录下找到package.json 添加

    "bootstrap": "^3.3.6",
    "jquery": "^2.1.4",

    版本可以根据自己的需要修改

    cnpm install

    在控制台下安装。

    3.7.2、导入jQuery与Bootstrap

    在main.js 导入 (注意导入是node_modules下的路径可以点进去查看具体位置)min是压缩后文件建议导入这个

    import 'jquery/dist/jquery.min.js'
    import 'bootstrap/dist/css/bootstrap.min.css'
    import 'bootstrap/dist/js/bootstrap.min.js' 

    3.7.3、使用内置插件ProvidePlugin自动加载模块

    此时jQuery并未依赖成功,将提示错误:

    插件ProvidePlugin(点击查看)自动加载模块,而不必到处 import 或 require。

    需在build/webpack.base.conf.js中增加插件配置

    const webpack = require('webpack')

    配置中添加

    plugins: [
      new webpack.ProvidePlugin({
        $: "jquery",
        jQuery: "jquery",
        "windows.jQuery": "jquery"
      })
    ],

    完整结果:

    'use strict'
    const path = require('path')
    const utils = require('./utils')
    const config = require('../config')
    const vueLoaderConfig = require('./vue-loader.conf')
    const webpack = require('webpack')
    
    function resolve (dir) {
      return path.join(__dirname, '..', dir)
    }
    
    const webpack = require('webpack')
    
    
    module.exports = {
      context: path.resolve(__dirname, '../'),
      entry: {
        app: './src/main.js'
      },
      output: {
        path: config.build.assetsRoot,
        filename: '[name].js',
        publicPath: process.env.NODE_ENV === 'production'
          ? config.build.assetsPublicPath
          : config.dev.assetsPublicPath
      },
      resolve: {
        extensions: ['.js', '.vue', '.json'],
        alias: {
          'vue$': 'vue/dist/vue.esm.js',
          '@': resolve('src'),
        }
      },
      module: {
        rules: [
          {
            test: /.vue$/,
            loader: 'vue-loader',
            options: vueLoaderConfig
          },
          {
            test: /.js$/,
            loader: 'babel-loader',
            include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]
          },
          {
            test: /.(png|jpe?g|gif|svg)(?.*)?$/,
            loader: 'url-loader',
            options: {
              limit: 10000,
              name: utils.assetsPath('img/[name].[hash:7].[ext]')
            }
          },
          {
            test: /.(mp4|webm|ogg|mp3|wav|flac|aac)(?.*)?$/,
            loader: 'url-loader',
            options: {
              limit: 10000,
              name: utils.assetsPath('media/[name].[hash:7].[ext]')
            }
          },
          {
            test: /.(woff2?|eot|ttf|otf)(?.*)?$/,
            loader: 'url-loader',
            options: {
              limit: 10000,
              name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
            }
          }
        ]
      },
      plugins: [
        new webpack.ProvidePlugin({
          $: "jquery",
          jQuery: "jquery",
          "windows.jQuery": "jquery"
        })
      ],
      node: {
        // prevent webpack from injecting useless setImmediate polyfill because Vue
        // source contains it (although only uses it if it's native).
        setImmediate: false,
        // prevent webpack from injecting mocks to Node native modules
        // that does not make sense for the client
        dgram: 'empty',
        fs: 'empty',
        net: 'empty',
        tls: 'empty',
        child_process: 'empty'
      }
    }
    View Code

    3.7.4、使用jQuery与Bootstrap

    index.html

    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width,initial-scale=1.0">
        <title>vuecli06</title>
      </head>
      <body>
        <div id="app">
          <ul class="nav nav-tabs">
            <li role="presentation" class="active"><a href="/">首页</a></li>
            <li role="presentation"><a href="#/a">组件A</a></li>
            <li role="presentation"><a href="#/b">组件B</a></li>
            <li role="presentation"><a href="#/c">组件C</a></li>
          </ul>
          <!--router-view主要是构建 SPA (单页应用) 时,方便渲染你指定路由对应的组件。你可以 router-view 当做是一个容器,它渲染的组件是你使用 vue-router 指定的。-->
          <router-view></router-view>
        </div>
        <!-- built files will be auto injected -->
      </body>
    </html>

    运行结果:

    四、示例下载

    https://git.coding.net/zhangguo5/vue2.git

    五、视频

    https://www.bilibili.com/video/av17503637/

    六、作业

    1、请定义任意一个Vue实例,触发Vue实例中的8个事件,将结果输出到控制台,要求可以观察到数据初始化与挂载情况。

    2、请使用vuecli+vue2+bootstrap完成如下功能:

    要求如下:

    a)、实现添加功能

    b)、实现删除功能

    c)、实现编辑功能,增加按钮

    d)、实现隔行换色

    e)、实现按年龄排序

    参考

  • 相关阅读:
    Js前端路由管理器函数
    js前端登录js脚本
    docker部署certbot与nginx来获取ssl证书添加https及自动更新
    spring boot不同版本的优雅关闭(graceful shutdown)和在windows下winsw服务方式运行的配置
    php下载
    在k8s中导出jvm内存错误dump文件到OSS
    基于alpine构建jdk镜像遇到的坑
    UML建模综述
    Web安全攻防渗透测试实战指南之工具
    数字签名、数字证书是什么?
  • 原文地址:https://www.cnblogs.com/best/p/8124786.html
Copyright © 2011-2022 走看看