Vue.js


Vue.js组件

组件注册

• 全局注册

全局注册的组件在注册后可以用于任意实例或组件中。

注意:全局注册必须设置在根 Vue 实例创建之前

• 组件基础

本质上,组件是可复用的 Vue 实例,所以它们可与 new Vue 接收相同的选项,例如 data、methods 以及生命周期钩子等。

仅有的例外是像 el 这样根实例特有的选项:	

组件具有两种命名规则:

kebab-case:'my-component’
PascalCase:'MyComponent'

组件命名规则:

Vue.component('my-component-a',{});
Vue.component('MyComponentB',{});

注意:无论采用哪种命名方式,在 DOM 中都只有 kebab-case 可以使用。

template 选项

template 选项用于设置组件的结构,最终被引入根实例或其他组件中

Vue.component('MyComA', {
      template: `
        <div>
          这是组件 A 的内容: {{ 1 + 2 * 3 }}
        </div>
      `
    });

注意:组件必须只有一个根元素。

data 选项

data 选项用于存储组件的数据,与根实例不同,组件的 data 选
项必须为函数,数据设置在返回值对象中。

Vue.component('MyComA', {
    template: `
       <div>
         <h3>{{ title }}</h3>
         <p>{{ content }}</p>
       </div>
     `,
     data () {
       return {
         title: '这是组件标题',
         content: '这是组件内容'
       }
     }
   });

这种实现方式是为了确保每个组件实例可以维护一份被返回对象
的独立的拷贝,不会相互影响。

• 局部注册

局部注册的组件只能用在当前实例或组件中。

<div id="app">
   <my-component-a></my-component-a>
   <my-component-b></my-component-b>
 </div>
var MyComponentA = {
    template: `
       <div>
         <h3>{{ title }}</h3>
         <p>{{ content }}</p>
       </div>
     `,
     data () {
       return {
         title: '组件 A 标题',
         content: '组件 A 内容'
       }
     }
   };

   // 组件 B 的选项对象
   var MyComponentB = {
     template: `
       <div>
         <h3>{{ title }}</h3>
         <p>{{ content }}</p>
       </div>
     `,
     data () {
       return {
         title: '组件 B',
         content: '组件 B 内容'
       }
     }
   }

   new Vue({
     el: '#app',
     data: {

     },
     components: {
       'my-component-a': MyComponentA,
       MyComponentB
     }
   });

组件通信

在组件间传递数据的操作,称为组件通信。

• 父组件向子组件传值

通过子组件的 props 选项接收父组件的传值
注意:props 不要与 data 存在同名属性
建议 prop 命名使用 camelCase,父组件绑定时使用 kebab-case

<div id="app">
    <!-- 通过 v-for 遍历数据 items,创建组件并生成内容 -->
    <demo-item
      v-for="item in items"
      :item-title="item.title"
      :item-content="item.content"
      :key="item.title"

      :item="item"
    ></demo-item>
  </div>
Vue.component('demoItem', {
      props: ['itemTitle', 'itemContent', 'item'],
      template: `
        <div>
          <h3>{{ itemTitle }}</h3>
          <p> {{ itemContent }} </p>
        </div>
      `
    })

    new Vue({
      el: '#app',
      data: {
        // 准备给子组件使用的数据
        items: [
          {
            title: '示例标题1',
            content: '示例内容1'
          },
          {
            title: '示例标题2',
            content: '示例内容2'
          },
          {
            title: '示例标题3',
            content: '示例内容3'
          },
        ]
      }
    })

单向数据流

父子组件间的所有 prop 都是单向下行绑定的。
如果子组件要处理 prop 数据,应当存储在 data 中后操作。
注意,如果 prop 为数组或对象时,子组件操作将会影响到父组件的状态。

<div id="app">
    <my-component
      :initial-title="title"
      :obj="obj"
    ></my-component>
  </div>
 Vue.component('my-component', {
      props: ['initialTitle', 'obj'],
      template: `
        <div>
          {{ title }}
          <button @click="fn">按钮</button>
        </div>
      `,
      data () {
        return {
          title: this.initialTitle
        }
      },
      methods: {
        fn () {
          // this.title = '这是新的标题';
          // this.initialTitle = '这是新的标题'; // 不会影响父组件
          this.obj.name = 'jack';

        }
      }
    });

    new Vue({
      el: '#app',
      data: {
        title: '这是示例标题',
        obj: {
          name: 'william',
          age: 18
        }
      }
    });

Props 类型

Prop 可以设置类型检查,这时需要将 props 更改为一个带有验
证需求的对象,并指定对应类型。

prop 还可以同时指定多个类型,通过数组方式保存即可。

<div id="app">
    <my-component
      :par-str="str"
      :par-num="num"
      :par-arr="arr"
      :par-obj="obj"
      :par-any="any"
      :par-data="str"
    ></my-component>
  </div>
Vue.component('MyComponent', {
      // 如果要设置 props 的具体规则,需要更改为对象写法
      props: {
        parStr: String,
        parNum: Number,
        parArr: Array,
        parObj: Object,
        parAny: undefined, // null
        parData: [String, Boolean]
      },
      template: `
        <div>
          {{ parStr }}
          {{ parNum }}
          {{ parArr }}
          {{ parObj }}
          {{ parAny }}
          {{ parData }}
        </div>
      `
    })

    new Vue({
      el: '#app',
      data: {
        num: 100,
        str: 'abc',
        arr: [1, 2, 3],
        obj: {
          content1: '示例内容1',
          content2: '示例内容2'
        },
        any: [1, 2, 3]
      }
    });

Props 验证
当 prop 需要设置多种规则时,可以将 prop 的值设置为选项对
象。
之前的类型检测功能通过 type 选项设置。

required 用于设置数据为必填项。
default 用于给可选项指定默认值,当父组件未传递数据时生效。
default 用于给可选项指定默认值,当父组件未传递数据时生效。
validator 用于给传入的 prop 设置校验函数,return 值为
false 时 Vue.js 会发出警告。
注意:验证函数中无法使用实例的 data、methods 等功能。

<div id="app">
    <my-component
      :par-str="str"
      :par-num="num"
      :par-num2="2000"
      :par-arr="arr"
      par-content="lagou: hello world"
    ></my-component>
  </div>
Vue.component('MyComponent', {
      props: {
        parStr: {
          type: String
        },
        parData: {
          type: [Array, Object]
        },
        parNum: {
          type: Number,
          required: true
        },
        parNum2: {
          type: Number,
          default: 100
        },
        parArr: {
          type: Array,
          default () {
            return [1, 2, 3];
          }
        },
        parContent: {
          type: String,
          validator (value) {
            console.log(this);
            return value.startsWith('lagou');
          }
        }
      },
      template: `<div></div>`
    })

    new Vue({
      el: '#app',
      data: {
        str: '示例内容',
        num: 100,
        arr: [10, 20, 30],
        obj: {
          content1: '内容1',
          content2: '内容2'
        }
      }
    });

非 Props 属性

当父组件给子组件设置了属性,但此属性在 props 中不存在,这
时会自动绑定到子组件的根元素上。

如果组件根元素已经存在了对应属性,则会替换组件内部的值。
class 与 style 是例外,当内外都设置时,属性会自动合并。

如果组件根元素已经存在了对应属性,则会替换组件内部的值。
class 与 style 是例外,当内外都设置时,属性会自动合并。

<div id="app">
    <my-component
      data-index="3"
      :title="'示例标题内容'"
      style="height: 200px;"
      class="colorRed"
    ></my-component>
  </div>
Vue.component('MyComponent', {
      inheritAttrs: false,
      template: `
      <div data-index="6"
           title="旧的title"
           class="abc" 
           style="width: 200px;">
        <p>这是组件的内容</p>
      </div>
      `
    });

    new Vue({
      el: '#app',
      data: {
        
      }
    });

• 子组件向父组件传值

子向父传值需要通过自定义事件实现。
子组件数据变化时,通过 $emit() 触发自定义事件。
自定义事件名称建议使用 kebab-case。
父组件监听子组件的自定义事件,并设置处理程序。

<div id="app">
    <h3>购物车</h3>
    <product-item
      v-for="product in products"
      :key="product.id"
      :title="product.title"

      @count-change="onCountChange"
    ></product-item>
    <p>商品总个数为:{{ totalCount }}</p>
  </div>
 // 子组件
    Vue.component('ProductItem', {
      props: ['title'],
      template: `
        <div>
          <span>商品名称: {{ title }}, 商品个数: {{ count }}</span>
          <button @click="countIns1">+1</button>
          <button @click="countIns5">+5</button>
        </div>
      `,
      data () {
        return {
          count: 0
        }
      },
      methods: {
        countIns1 () {
          this.$emit('count-change', 1);
          this.count++;
        },
        countIns5 () {
          this.$emit('count-change', 5);
          this.count += 5;
        }
      }
    });

    // 父组件
    new Vue({
      el: '#app',
      data: {
        products: [
          {
            id: 1,
            title: '苹果一斤'
          },
          {
            id: 2,
            title: '香蕉一根'
          },
          {
            id: 3,
            title: '橙子一个'
          }
        ],
        totalCount: 0
      },
      methods: {
        onCountChange (productCount) {
          this.totalCount += productCount;
        }
      }
    });

自定义事件传值

子组件触发事件时可以向父组件传值。
父组件在监听事件时需要接收子组件传递的数据。

组件与 v-mode

v-model 用于组件时,需要通过 props 与自定义事件实现。

<div id="app">
    <p>输入框内容为:{{ iptValue }}</p>
    <com-input v-model="iptValue"></com-input>
  </div>
// 子组件
    var ComInput = {
      props: ['value'],
      template: `
        <input
          type="text"
          :value="value"
          @input="onInput"
        >
      `,
      methods: {
        onInput (event) {
          this.$emit('input', event.target.value)
        }
      }
    }

    // 根实例
    new Vue({
      el: '#app',
      data: {
        iptValue: ''
      },
      components: {
        ComInput
      }
    });

• 非父子组件传值

非父子组件指的是兄弟组件或完全无关的两个组件。

  1. 兄弟组件传值

兄弟组件可以通过父组件进行数据中转

  1. EventBus–非父子组件传值

当组件嵌套关系复杂时,根据组件关系传值会较为繁琐。
组件为了数据中转,data 中会存在许多与当前组件功能无关的数据。

EventBus (事件总线)是一个独立的事件中心,用于管理不同组件间的传值操作。

EventBus 通过一个新的 Vue 实例来管理组件传值操作,组件通过给实例注册事件、调用事件来实现数据传递。

发送数据的组件触发 bus 事件,接收的组件给 bus 注册对应事件。

给 bus 注册对应事件通过 $on() 操作。

最后创建根实例执行代码即可。

<div id="app">
    <h3>购物车</h3>
    <product-item></product-item>
    <product-total></product-total>
  </div>
 // 商品组件
    Vue.component('ProductItem', {
      template: `
        <div>
          <span>商品名称:苹果,商品个数:{{ count }}</span>
          <button
            @click="countIns"
          >+1</button>
        </div>
      `,
      data () {
        return {
          count: 0
        }
      },
      methods: {
        countIns () {
          // 给bus触发自定义事件,传递数据
          bus.$emit('countChange', 1);
          this.count++;
        }
      }
    });

    // 计数组件
    Vue.component('ProductTotal', {
      template: `
        <p>总个数为: {{ totalCount }}</p>
      `,
      data () {
        return {
          totalCount: 0
        }
      },
      created () {
        // 给 bus 注册事件,并接收数据
        bus.$on('countChange', (productCount) => {
          // 实例创建完毕,可以使用 data 等功能
          this.totalCount += productCount;
        });

      }
    })

    // 根实例
    new Vue({
      el: '#app',
      data: {

      }
    });

• 其他通信方式

  1. $root

$root 用于访问当前组件树根实例,设置简单的 Vue 应用时可以通过此方式进行组件传值。

除了 $root , Vue.js 中还提供了 $parent 与 $children 用于
便捷访问父子组件。

<div id="app">
    <com-a></com-a>
  </div>
 // 根实例的子组件A的子组件B
    var ComB = {
      template: `
        <div>
          组件B: {{ $root.count }}
          <button @click="clickFn">按钮</button>
        </div>
      `,
      methods: {
        clickFn () {
          this.$root.count = 200;
        }
      }
    };

    // 子组件A
    var ComA = {
      template: `
        <div>
          组件A: {{ $root.count }}
          <button @click="clickFn">按钮</button>
          <com-b></com-b>
        </div>
      `,
      methods: {
        clickFn () {
          this.$root.count = 100;
        }
      },
      components: {
        ComB
      }
    };


    // 根实例
    new Vue({
      el: '#app',
      data: {
        count: 0
      },
      components: {
        ComA
      }
    });
  1. $refs

$refs 用于获取设置了 ref 属性的 HTML 标签或子组件
给普通 HTML 标签设置 ref 属性,$refs 可以获取 DOM

给子组件设置 ref 属性,渲染后可通过 $refs 获取子组件实例。

<div id="app">
    <!-- 给 HTML 标签设置 ref 属性 -->
    <input type="text" ref="inp">
    <button @click="fn">按钮</button>

    <!-- 给子组件设置 ref 属性 -->
    <com-a ref="comA"></com-a>
  </div>
var ComA = {
      template: `<div>组件A的内容:{{ value }}</div>`,
      data () {
        return {
          value: '示例内容'
        }
      }
    }

    var vm = new Vue({
      el: '#app',
      methods: {
        fn () {
          // 点击后修改 HTML 标签焦点状态
          this.$refs.inp.focus();

          this.$refs.comA.value = '新的内容';
        }
      },
      components: {
        ComA
      },
      mounted () {
        console.log(this.$refs);
        this.$refs.comA.value = "修改后的内容";
      },
    });

组件插槽

• 单个插槽
要通过 <slot> 进行插槽设置

 Vue.component('ComA', {
      template: `
        <div>
          <h3>组件标题</h3>
          <slot>
            这是插槽的默认内容
          </slot>
        </div>
      `,
      data () {
        return {
          value: '子组件的数据'
        }
      }
    });

    new Vue({
      el: '#app',
      data: {
        parValue: '这是父组件的数据'
      }
    })

• 具名插槽
如果组件中有多个位置需要设置插槽,据需要给 设置
name,称为具名插槽。

<div id="app">
    <com-a>
      <template v-slot:header>
        <h3>组件的头部内容</h3>
      </template>

      <!-- <template v-slot:default>
        <p>组件的主体内容1</p>
        <p>组件的主体内容2</p>
      </template> -->

      <p>组件的主体内容1</p>
      <p>组件的主体内容2</p>

      <template #footer>
        <p>组件底部内容</p>
      </template>
    </com-a>
  </div>
 Vue.component('ComA', {
      template: `
        <div>
          <header>
            <slot name="header"></slot>
          </header>
          <main>
            <slot></slot>
          </main>
          <footer>
            <slot name="footer"></slot>
          </footer>
        </div>
      `
    });

    new Vue({
      el: '#app',
      data: {

      }
    });

• 作用域插槽

用于让插槽可以使用子组件的数据。

组件将需要被插槽使用的数据通过 v-bind 绑定给 ,这种
用于给插槽传递数据的属性称为插槽 prop

组件绑定数据后,插槽中需要通过 v-slot 接收数据。

<div id="app">
    <!-- 多个插槽的作用域插槽书写方式 -->
    <com-a>
      <template v-slot:default="dataObj">
        {{ dataObj.value }}
        {{ dataObj.num }}
      </template>
      
      <template v-slot:footer="dataObj">
        {{ dataObj.value }}
      </template>
    </com-a>

    <!-- 只具有默认插槽的作用域插槽书写方式 -->
    <!-- <com-b v-slot="dataObj"> -->
    <com-b #default="dataObj">
      {{ dataObj }}
    </com-b>

    <!-- 通过 ES6 的解构操作接收作用域插槽的数据 -->
    <com-b v-slot="{ value, num }">
      {{ value }}
      {{ num }}
    </com-b>
  </div>
// 子组件B的选项对象
     var ComB = {
      template: `
        <div>
          <p>组件B的内容: </p>
          <slot
            :value="value"
            :num="num"
          ></slot>
        </div>
      `,
      data () {
        return {
          value: '这是组件B内部的数据',
          num: 200
        }
      }
    };


    // 子组件A的选项对象
    var ComA = {
      template: `
        <div>
          <p>组件A的内容: </p>
          <slot
            v-bind:value="value"
            :num="num"
          ></slot>
          <slot name="footer"
            :value="value"
          ></slot>
        </div>
      `,
      data () {
        return {
          value: '这是组件A内部的数据',
          num: 100
        }
      }
    };

    new Vue({
      el: '#app',
      components: {
        ComA, ComB
      }
    });

内置组件

• 动态组件

动态组件适用于多个组件频繁切换的处理。
<component> 用于将一个‘元组件’渲染为动态组件,以 is 属
性值决定渲染哪个组件。
is 属性会在每次切换组件时,Vue 都会创建一个新的组件实例。

<div id="app">
    <!-- 按钮代表选项卡的标题功能 -->
    <button
      v-for="title in titles"
      :key="title"
      @click="currentCom = title"
    >
      {{ title }}
    </button>

    <!-- component 设置动态组件 -->
    <component :is="currentCom"></component>
  </div>
  // 设置要切换的子组件选项对象
    var ComA = {
      template: `<p>这是组件A的内容:<input type="text"></p>`
    };  
    var ComB = {
      template: `<p>这是组件B的内容:<input type="text"></p>`
    };
    var ComC = {
      template: `<p>这是组件C的内容:<input type="text"></p>`
    };

    new Vue({
      el: '#app',
      data: {
        // 所有组件名称
        titles: ['ComA', 'ComB', 'ComC'],
        // 当前组件名称
        currentCom: 'ComA'
      },
      components: {
        ComA, ComB, ComC
      }
    });

• keep-alive 组件

主要用于保留组件状态或避免组件重新渲染。

include 属性用于指定哪些组件会被缓存,具有多种设置方式
exclude 属性用于指定哪些组件不会被缓存。

max 属性用于设置最大缓存个数。

<div id="app">
    <button 
      v-for="title in titles"
      :key="title"
      @click="currentCom = title"
    >
      {{title}}
    </button>

    <!-- 通过 include 设置哪些组件会被缓存 -->
    <!-- <keep-alive include="ComA,ComB,ComC"> -->
    <!-- <keep-alive :include="['ComA', 'ComB', 'ComC']"> -->
    <!-- <keep-alive :include="/Com[ABC]/"> -->

    <!-- 通过 exclude 设置哪些组件不会被缓存 -->
    <!-- <keep-alive exclude="ComD"> -->
    <!-- <keep-alive :exclude="['ComD']"> -->
    <!-- <keep-alive :exclude="/ComD/"> -->

    
    <keep-alive max="2">
      <!-- 动态组件 -->
      <component :is="currentCom"></component>
    </keep-alive>
  </div>
var ComA = {
      template: `
        <div>
          请选择主食:
          <br>
          <label for="mantou">馒头:</label>
          <input id="mantou" type="radio" name="zhushi" value="mantou">
          <br>
          <label for="mifan">米饭:</label>
          <input id="mifan" type="radio" name="zhushi" value="mifan">
        </div>
      `
    };
    var ComB = {
      template: `
        <div>
          请选择菜品:
          <br>
          <label for="luobo">炒萝卜:</label>
          <input id="luobo" type="checkbox" name="cai" value="luobo">
          <br>
          <label for="niurou">炒牛肉:</label>
          <input id="niurou" type="checkbox" name="cai" value="niurou">
          <br>
          <label for="pingguo">炒苹果:</label>
          <input id="pingguo" type="checkbox" name="cai" value="pingguo">
        </div>
      `
    };
    var ComC = {
      template: `
        <div>
          请选择汤:
          <br>
          <label for="tang1"">西红柿鸡蛋汤:</label>
          <input id="tang1"" type="radio" name="tang" value="tang1"">
          <br>
          <label for="tang2">紫菜蛋花汤:</label>
          <input id="tang2" type="radio" name="tang" value="tang2">
          <br>
          <label for="tang3">清汤</label>
          <input id="tang3" type="radio" name="tang" value="tang3">
        </div>
      `
    };

    var ComD = {
      template: `
        <div>
          请输入支付信息:
          <br>
          <label for="account"">请输入账号:</label>
          <input id="account"" type="text" name="account">
          <br>
          <label for="password">请输入密码:</label>
          <input id="password" type="password" name="password">
          <br>
        </div>
      
      `
    };

    new Vue({
      el: '#app',
      data: {
        titles: ['ComA', 'ComB', 'ComC', 'ComD'],
        currentCom: 'ComA'
      },
      components: {
        ComA, ComB, ComC, ComD
      }
    });

• 过渡组

用于在 Vue 插入、更新或者移除 DOM 时,提供多种不同方式的应用过渡、动画效果.

• transition 组件

用于给元素和组件添加进入/离开过渡:

组件提供了 6个 class,用于设置过渡具体效果

    /* 用于设置出场的最终状态 */
    .v-leave-to {
      opacity: 0;
    }

    /* 用于设置过渡的执行过程 */
    .v-leave-active {
      transition: opacity 1s;
    }

    /* 用于设置入场的初始状态 */
    .v-enter {
      opacity: 0;
    }

    /* 用于设置入场的最终状态 */
    .v-enter-to {
      opacity: 0.5;
    }

    /* 用于设置入场的过程 */
    .v-enter-active {
      transition: all 1s;
    }
   
<div id="app">
    <button @click="show = !show">切换</button>

    <transition>
      <p v-if="show">hello world</p>
    </transition>
  </div>  
  <script src="./lib/vue.js"></script>
  <script>
    new Vue({
      el: '#app',
      data: {
        show: true
      }
    });
  </script>

相关属性

给组件设置 name 属性,可用于给多个元素、组件设置不同的过
渡效果,这时需要将 v- 更改为对应 name- 的形式

    /* 第一组过渡效果设置 */
    .v-enter, .v-leave-to {
      opacity: 0;
    }

    .v-enter-active, .v-leave-active {
      transition: opacity .5s;
    }

    /* 第二组过渡效果设置 */
    .demo-enter, .demo-leave-to {
      opacity: 0;
      transform: translateX(200px);
    }

    .demo-enter-active, .demo-leave-active {
      transition: all .5s;
    }
  <button @click="show = !show">切换1</button>
    <!-- 没有设置 name 命名的 transition 组件,类名采用 v- 前缀 -->
    <transition appear>
      <p v-if="show">这是要切换的元素1</p>
    </transition>

    <br>

    <button @click="showDemo = !showDemo">切换2</button>
    <!-- 设置了 name 的 transition 组件,类名需要将 v- 修改为 demo- -->
    <transition 
      name="demo"
      appear
      >
      <p v-if="showDemo">这是要切换的元素2</p>
    </transition>
  </div>  
  <script src="./lib/vue.js"></script>
  <script>
    new Vue({
      el: '#app',
      data: {
        show: true,
        showDemo: true
      }
    });
  </script>

• 自定义过渡类名
自定义类名比普通类名优先级更高,在使用第三方 CSS 动画库时非常有用

自定义过渡类名

用于设置自定义过渡类名的属性如下:
• enter-class
• enter-active-class
• enter-to-class
• leave-class
• leave-active-class
• leave-to-class

用于设置初始过渡类名的属性如下:
• appear-class
• appear-to-class
• appear-active-class

Animate.css 是一个第三方 CSS 动画库,通过设置类名来给元素添加各种动画效果。
使用注意:
• animate__ 前缀与 compat 版本
• 基础类名 animate

<style>
    .v-enter, .v-leave-to {
      opacity: 0;
    }

    .v-enter-active, .v-leave-active {
      transition: all .5s;
    }

    .test {
      transition: all 3s;
    }

  </style>
</head>
<body>
  <div id="app">
    <button @click="show = !show">切换</button>

    <transition
      enter-active-class="test"
      leave-active-class="test"
    >
      <p v-if="show">这是 p 标签</p>
    </transition>
  </div>  
  <script src="./lib/vue.js"></script>
  <script>
    new Vue({
      el: '#app',
      data: {
        show: true
      }
    });
  </script>

• transition-group 组件

<transition-group> 用于给列表统一设置过渡动画。
• tag 属性用于设置容器元素,默认为 。
• 过渡会应用于内部元素,而不是容器。
• 子节点必须有独立的 key,动画才能正常工作。

当列表元素变更导致元素位移,可以通过 .v-move 类名设置移动时的效果

 ul {
      position: relative;
    }

    .v-enter, .v-leave-to {
      opacity: 0;
      transform: translateX(100px);
    }

    .v-enter-active, .v-leave-active {
      transition: all .5s;
    }

    /* 让元素在离场的过程中脱离标准流 */
    .v-leave-active {
      position: absolute;
    }

    .v-move {
      transition: all .5s;
    }

  </style>
</head>
<body>
  <div id="app">
    <input type="text"
      v-model="newTitle"
      @keyup.enter="addItem"
    >

    <transition-group
      tag="ul"
    >
      <li
        v-for="item in items"
        :key="item.id"
        @click="removeItem(item)"
      >
        {{ item.title }}
      </li>
    </transition-group>
  </div>  
  <script src="./lib/vue.js"></script>
  <script>
    new Vue({
      el: '#app',
      data: {
        items: [
          { id: 1, title: '示例内容1'},
          { id: 2, title: '示例内容2'},
          { id: 3, title: '示例内容3'},
          { id: 4, title: '示例内容4'},
          { id: 5, title: '示例内容5'},
        ],
        newTitle: '',
        latestId: 5
      },
      methods: {
        addItem () {
          this.items.push({
            id: this.latestId + 1,
            title: this.newTitle
          });
          this.latestId++;
          this.newTitle = '';
        },
        removeItem (item) {
          var i = this.items.indexOf(item);
          this.items.splice(i, 1);
        }
      }
    });
  </script>

Vue Router

Vue Router 是 Vue.js 的官方插件,用来快速实现单页应用。

单页应用

SPA(Single Page Application)单页面应用程序,简称单页应用。
指的是网站的 “所有” 功能都在单个页面中进行呈现。
具有代表性的有后台管理系统、移动端、小程序等。

优点:
• 前后端分离开发,提高了开发效率。
• 业务场景切换时,局部更新结构。
• 用户体验好,更加接近本地应用。

缺点:
• 不利于 SEO。
• 初次首屏加载速度较慢。
• 页面复杂度比较高。

前端路由

前端路由,指的是 URL内容间的映射关系

• Hash 方式

通过 hashchange 事件监听 hash 变化,并进行网页内容更新

特点总结:
• Hash 方式兼容性好。
• 地址中具有 #,不太美观。
• 前进后退功能较为繁琐。

<div>
    <a href="#/">首页</a>
    <a href="#/category">分类页</a>
    <a href="#/user">用户页</a>
  </div>
  <div id="container">
    这是首页功能
  </div>

// 准备对象,用于封装 hash 功能。
    var router = {
      // 路由存储位置: 保存了 url 与 内容处理函数的对应关系。
      routes: {},
      // 定义路由规则的方法
      route: function (path, callback) {
        this.routes[path] = callback;
      },
      // 初始化路由的方法
      init: function () {
        var that = this;
        window.onhashchange = function () {
          // 当 hash 改变,我们需要得到当前新的 hash
          var hash = location.hash.replace('#', '');
          // 根据 hash 触发 routes 中的对应 callback
          that.routes[hash] && that.routes[hash]();
        };
      }
    };

    var container = document.getElementById('container');
    // 定义路由规则
    router.route('/', function () {
      container.innerHTML = '这是首页功能';
    });
    router.route('/category', function () {
      container.innerHTML = '这是分类功能';
    });
    router.route('/user', function () {
      container.innerHTML = '这是用户功能';
    });

    // 初始化路由
    router.init();

•History 方式

History 方式采用 HTML5 提供的新功能实现前端路由
在操作时需要通过 history.pushState() 变更 URL并执行对应操
作。

 var router = {
      // 存储路由的对象
      routes: {},
      // 定义路由的方法
      route (path, callback) {
        this.routes[path] = callback;
      },
      // 用于触发指定的路由操作
      go (path) {
        // 更改 url
        history.pushState({ path: path }, null, path);
        // 触发路由对应的回调函数
        this.routes[path] && this.routes[path]();
      },
      // 设置初始化方法,用来检测前进后退按钮的功能
      init () {
        var that = this;
        window.addEventListener('popstate', function (e) {
          var path = e.state ? e.state.path : '/';
          that.routes[path] && that.routes[path]();
        });
      }
    };

    router.init();

    // 设置 a 标签的功能
    var links = document.querySelectorAll('a');
    var container = document.querySelector('#container');

    links.forEach(function (ele) {
      ele.addEventListener('click', function (event) {
        router.go(this.getAttribute('href'));
        event.preventDefault();
      });
    });

    // 路由规则
    router.route('/', function () {
      container.innerHTML = '首页功能';
    });

    router.route('/category', function () {
      container.innerHTML = '分类功能';
    });

    router.route('/user', function () {
      container.innerHTML = '用户功能';
    });

Vue Router

是 Vue.js 官方的路由管理器,让构建单页面应用变得易如反掌。
• 基本使用

直接下载 / CDN

• 最新版本:https://unpkg.com/vue-router/dist/vue-router.js
• 指定版本:https://unpkg.com/vue-router@3.4.9/dist/vue-router.js

npm
• npm install vue-router

Vue Router 提供了用于进行路由设置的组件 <router-link> 与
<router-view>。

<div id="app">
    <!-- 设置用于进行路由操作的组件 -->
    <!-- <router-link to="/">首页</router-link>
    <router-link to="/user">用户</router-link>
    <router-link to="/category">分类</router-link>

    <router-view></router-view> -->
  </div>
  <script src="lib/vue.js"></script>
  <script src="lib/vue-router.js"></script>
  <script>
    var Index = {
      template: `<div>首页功能</div>`
    };
    var User = {
      template: `<div>用户功能</div>`
    };
    var Category = {
      template: `<div>分类功能</div>`
    };

    // 定义路由规则
    var routes = [
      { path: '/', component: Index },
      { path: '/user', component: User },
      { path: '/category', component: Category }
    ];

    // 创建 Vue Router 实例
    var router = new VueRouter({
      routes
    });

    // 创建 Vue 实例,注入 router
    var vm = new Vue({
      el: '#app',
      // router
    });

    console.log(vm);
    
  </script>

命名视图

如果导航后,希望同时在同级展示多个视图(组件),这时就需
要进行命名视图。
路由中通过 components 属性进行设置不同视图的对应组件。

<div id="app">
    <router-link to="/">首页</router-link>
    <router-link to="/user">用户</router-link>

    <router-view name="sidebar"></router-view>
    <!-- 没有设置 name 的 router-view 默认 name 为 default-->
    <router-view></router-view>
  </div>
  <script src="lib/vue.js"></script>
  <script src="lib/vue-router.js"></script>
  <script>
    var SideBar1 = {
      template: `<div>侧边栏1功能</div>`
    };

    var SideBar2 = {
      template: `<div>侧边栏2功能</div>`
    };

    var Index = {
      template: `<div>首页功能</div>`
    };

    var User = {
      template: `<div>用户功能</div>`
    };

    // 定义路由规则
    var routes = [
      {
        path: '/',
        components: {
          // router-view 的 name : 组件配置对象
          default: Index,
          sidebar: SideBar1
        }
      },
      {
        path: '/user',
        components: {
          default: User,
          sidebar: SideBar2
        }
      }
    ];

    // 创建 Vue Router 实例
    var router = new VueRouter({
      routes
    });

    // 创建 Vue 实例
    new Vue({
      el: '#app',
      router
    });
  </script>

• 动态路由

当我们需要将某一类 URL 都映射到同一个组件,就需要使用动态路由。
定义路由规则时,将路径中的某个部分使用 : 进行标记,即可
设置为动态路由。
设置为动态路由后,动态部分为任意内容均跳转到同一组件。
: 部分对应的信息称为路径参数,存储在 vm.$route.params 中。

 <div id="app">
    <router-link to="/user/1">用户1</router-link>
    <router-link to="/user/2">用户2</router-link>
    <router-link to="/user/3">用户3</router-link>

    <router-view></router-view>

  </div>
  <script src="lib/vue.js"></script>
  <script src="lib/vue-router.js"></script>
  <script>
    // 设置组件 
    var User = {
      template: `<div>这是用户 {{ $route.params.id }} 的功能</div>`
    };

    // 设置路由规则
    var routes = [
      {
        path: '/user/:id', component: User
      }
    ];

    var router = new VueRouter({ routes });
    var vm = new Vue({
      el: '#app',
      router
    });
  </script>

侦听路由参数

如果要响应路由的参数变化,可以通过 watch 监听 $route

// 设置组件 
    var User = {
      template: `
        <div>
          这是用户 {{ $route.params.id }} 的功能
          <input type="text">
        </div>`,
      // 由于组件没有重新创建,所以生命周期钩子只能执行一次
      /* created () {
        console.log('创建了组件的实例');
      } */
      watch: {
        $route (route) {
          // console.log(route);
          console.log(route.params.id)
        }
      }
    };

    // 设置路由规则
    var routes = [
      {
        path: '/user/:id', component: User
      }
    ];

    var router = new VueRouter({ routes });
    var vm = new Vue({
      el: '#app',
      router
    });

路由传参处理

这里通过路由的 props 设置数据,并通过组件 props接收
包含多个命名视图时,需要将路由的 props 设置为对象
如果希望设置静态数据,可将 props 中的某个组件对应的选项设
置为对象,内部属性会绑定给组件的 props

// 组件的配置对象
    var User = {
      template: `<div>这是用户 {{ $route.params.id }} 功能</div>`
    };

    var Category = {
      props: ['id'],
      template: `<div>这是分类 {{ id }} 功能</div>`
    };

    // 设置路由规则
    var routes = [
      {
        path: '/user/:id',
        component: User
      },
      {
        path: '/category/:id',
        component: Category,
        props: true
      }
    ];

    var router = new VueRouter({ routes });
    var vm = new Vue({
      el: '#app',
      router
    });

• 嵌套路由

实际场景中,路由通常由多层嵌套的组件组合而成,这时需要使用嵌套路由配置。

使用 children 来进行嵌套路由中的子路由设置

<div id="app">
    <router-link to="/user">用户功能</router-link>
    <router-view></router-view>
  </div>
  <script src="./lib/vue.js"></script>
  <script src="./lib/vue-router.js"></script>
  <script>
    var User = {
      template: `
        <div>
          <h3>这是 User 组件的功能</h3>
          <router-link to="/user/hobby">爱好功能</router-link>
          <router-link to="/user/info">用户信息</router-link>
          <router-view></router-view>
        </div>
      `
    };

    var UserHobby = {
      template: `<div> UserHobby 组件</div>`
    };

    var UserInfo = {
      template: `
        <div> 
          UserInfo 组件
          <router-link to="/user/info/school">学校信息</router-link>
          <router-view></router-view>
        </div>`
    };

    var UserInfoSchool = {
      template: `<div> UserInfoSchool 组件</div>`
    };

    var routes = [
      {
        path: '/user',
        component: User,
        children: [
          {
            path: 'hobby',
            component: UserHobby
          },
          {
            path: 'info',
            component: UserInfo,
            children: [
              {
                path: 'school',
                component: UserInfoSchool
              },
            ]
          }
        ]
      }
    ];

    var router =  new VueRouter({ routes });
    var vm = new Vue({
      el: '#app',
      router
    });
  </script>

• 编程式导航(通过方法设置导航)

router.push() 用来导航到一个新 URL
<router-link> 的 to 属性使用绑定方式时也可属性对象结

<div id="app">
    <!-- 声明式导航 -->
    <!-- <router-link to="/user/200">用户200</router-link> -->
    <router-link :to="{ path: '/user/700' }">用户700</router-link>

    <router-view></router-view>
  </div>
  <script src="./lib/vue.js"></script>
  <script src="./lib/vue-router.js"></script>
  <script>
    var User = {
      template: `<div> 这是用户 {{ $route.params.id }} </div>`
    };

    var routes = [
      {
        path: '/user/:id',
        component: User
      }
    ];

    var router = new VueRouter({ routes });
    var vm = new Vue({
      el: '#app',
      router
    });
  </script>

命名路由
设置路由时添加 name 属性
在 push() 中通过 name 导航到对应路由,参数通过 params 设
置。
也可以在 <router-link> 中使用

<div id="app">
    <router-link :to="{ name: 'school', params: { id: 10 } }">学校10</router-link>

    <router-view></router-view>
  </div>
  <script src="lib/vue.js"></script>
  <script src="lib/vue-router.js"></script>
  <script>
    var School = {
      template: `<div>School 组件的功能: {{ $route.params }}</div>`
    };

    var routes = [
      {
        path: '/user/:id/info/school',
        name: 'school',
        component: School
      }
    ];

    var router = new VueRouter({ routes });
    var vm = new Vue({
      el: '#app',
      router
    });


  </script>

重定向

<div id="app">
    <router-link to="/">首页</router-link>
    <router-link to="/category/2">分类2</router-link>
    <router-link to="/category"> /category </router-link>

    <router-view></router-view>
  </div>
  <script src="./lib/vue.js"></script>
  <script src="./lib/vue-router.js"></script>
  <script>
    var Index = {
      template: `<div>首页功能</div>`
    };

    var Category = {
      template: `<div>分类 {{ $route.params.id }} 功能</div>`
    };

    var router = new VueRouter({
      routes: [
        {
          path: '/',
          component: Index
        },
        {
          path: '/category/:id',
          component: Category
        },
        {
          path: '/category',
          redirect: '/'
        }
      ]
    });

    var vm = new Vue({
      el: '#app',
      router
    });
  </script>

别名

<div id="app">
    <router-link :to="{ name: 'school', params: { id: 10, date: '0612'} }">学校信息</router-link>
    <router-link to="/20/1234">学校信息2</router-link>
    <router-view></router-view>
  </div>
  <script src="lib/vue.js"></script>
  <script src="lib/vue-router.js"></script>
  <script>
    // 组件
    var School = {
      template: `
        <div>School 组件</div>
      `
    };

    // 路由规则
    var router = new VueRouter({
      routes: [
        {
          path: '/user/:id/info/school/:date',
          name: 'school',
          component: School,
          alias: '/:id/:date'
        }
      ]
    });

    var vm = new Vue({
      el: '#app',
      router
    });
  </script>

导航守卫

<div id="app">
    <router-link to="/">首页</router-link>
    <router-link to="/user">用户</router-link>
    <router-link to="/category">分类</router-link>
    <router-view></router-view>
  </div>
  <script src="lib/vue.js"></script>
  <script src="lib/vue-router.js"></script>
  <script>
    var Index = {
      template: `<div>这是首页功能</div>`
    };
    var User = {
      template: `<div>这是用户功能</div>`
    };
    var Category = {
      template: `<div>这是分类功能</div>`
    };

    var router = new VueRouter({
      routes: [
        { path: '/', component: Index },
        { path: '/user', component: User },
        { path: '/category', component: Category },
      ]
    });

    // 设置导航守卫
    router.beforeEach(function (to, from, next) {
      // console.log(to, from);
      // next();
      // next(false);

      if (to.path === '/user') {
        next('/category');
      } else {
        next();
      }
    });

    var vm = new Vue({
      el: '#app',
      router
    })
  </script>

Vue CLI

Vue CLI 是一个基于 Vue.js 进行快速开发的完整系统,称为脚手架工具。

安装

• 安装
• npm install –g @vue/cli
• 升级
• npm update –g @vue/cli

项目搭建

创建项目:
• vue create project-demo
选择 Preset、包管理器
创建完成

运行项目:
• npm run serv

目录与文件

单文件组件可以将组件的功能统一保存在以 .vue 为扩展名的文
件中。

打包与部署

打包就是将 Vue CLI 项目编译为浏览器可识别的文件。
命令:
• npm run build

部署指的是将 Vue 项目dist 目录部署到服务器上。
安装静态文件服务器:
• npm install –g serve
在 dist 下通过 serve 命令部

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值