Vue 2.0


# 插槽

当父组件想展示给子组件不同的结构 (div,span……) 时,可以使用插槽

# 普通插槽 弹窗简单示例 (可以看一下)

  • 子文件

    <template>
      <div v-show="visibility" class="dialog-wrap">
        <div class="dialog-body">
          <slot></slot>
        </div>
      </div>
    </template>
    
    <script>
      export default {
        name: "Dialog",
        props: {
          visibility: {
            type: Boolean,
            default: false,
          },
        },
      };
    </script>
  • 父文件

    <template>
      <div id="app">
        <dialog :visibility="visibility">
          <p>是否删除?</p>
        </dialog>
      </div>
    </template>
    
    <script>
      import Dialog from "./components/DialogDemo/Dialog";
      export default {
        name: "App",
        components: { Dialog },
        data() {
          return {
            visibility: false,
          };
        },
      };
    </script>

# 命名 (具名) 插槽

  • 父组件

    <template>
      <div id="app">
        <dialog :visibility="visibility_other" title="新增操作">
          <!-- 默认的具名插槽,什么name都不起,默认使用 slot -->
          <label for>活动名称</label>
          <input type="text" />
    
          <!-- <template v-slot:footer> -->
          <!-- 具名插槽的简写 v-slot: 变成 # -->
          <template #footer>
            <button @click="close_visibility_other">取消</button>
            <button>确定</button>
          </template>
        </dialog>
      </div>
    </template>
    
    <script>
      import Dialog from "./components/DialogDemo/Dialog";
      export default {
        name: "App",
        components: {
          Dialog,
        },
        data() {
          return {
            visibility_other: false,
          };
        },
        methods: {
          close_visibility_other() {
            this.visibility_other = false;
          },
        },
      };
    </script>
  • 子组件

    <template>
      <div v-show="visibility" class="dialog-wrap">
        <div class="dialog">
          <div class="dialog-header">
            <span>{{title}}</span>
          </div>
          <div class="dialog-body">
            <!-- 内容是自定制的(父传什么就是什么) -->
            <!-- <slot name="body"></slot> -->
            <slot />
          </div>
          <div class="dialog-footer">
            <!-- 内容是自定制的(父传什么就是什么) -->
            <!-- 所以这里面如果出现了按钮,事件就不需要传递给 dialog 直接在传递之前绑定就行了 -->
            <slot name="footer"></slot>
          </div>
        </div>
      </div>
    </template>
    
    <script>
      export default {
        name: "Dialog",
        props: {
          visibility: {
            type: Boolean,
            default: false, // 默认值(默认值是对象类的时候必须使用函数返回该对象),非必须的
          },
          title: {
            type: [Number, String],
            default: "默认的标题",
          },
        },
      };
    </script>

# 编译作用域和后备内容

  • SlotDemo.vue 父组件

    <template>
      <div>
        <h3>作用域插槽</h3>
        <!-- 给 post 组件传递的插槽内容的作用域是父组件作用域 -->
        <!-- 父级模板里的所有内容都是在父级作用域中编译的 -->
        <!-- 子模板里的所有内容都是在子作用域中编译的 -->
        <!-- 插槽内无法使用给子组件传递的属性和属性值 -->
        <Post title="哈哈哈">
          <!-- Post底下的拿不到Post传的内容 -->
          <!-- 这里的Post是传递给子级的,子级可以使用 -->
          <!-- {{title}} 这里的title是拿不到的 -->
          <template #content>
            <!-- 这里拿不到上边传的title -->
            <p>第一段</p>
            <p>第二段</p>
          </template>
    
          <ul>
            <li>{{commentText}}</li>
          </ul>
        </Post>
      </div>
    </template>
    
    <script>
      import Post from "./Post";
      export default {
        name: "SlotDemo",
        components: { Post },
        data() {
          return {
            commentText: "666",
          };
        },
      };
    </script>
  • Post.vue 子组件

    <template>
      <div class="title">{{title}}</div>
      <div class="content">
        <slot name="content" />
      </div>
      <div class="comments">
        <!-- 插槽的后备内容 可以实现默认展示
        插槽没有传递的时候展示后备内容 -->
        <!-- 如果父组件中的ul不传过来,那么就显示暂无评论,如果上边传了,就显示上边传的内容 -->
        <slot name="default">暂无评论</slot>
      </div>
    </template>
    
    <script>
      export default {
        name: "Post",
        props: ["title"],
      };
    </script>

# 作用域插槽

在父组件中想要使用子组件内容
让插槽内容能够访问子组件中才有的数据
子组件插槽里可以传很多属性,父组件使用时,用 template v-slot 接收时是接收的一个对象,这里边包含着子组件传的属性

  • 案例一

    • SlotDemo.vue 父组件

      <template>
        <div>
          <h3>作用域插槽</h3>
          <!-- 使用 v-slot 接收子组件内的插槽 prop -->
          <!-- v-slot:default='props对象' -->
          <!-- props对象 包含所有的 插槽 prop -->
          <!-- 子组件中的 :user="user" 叫做插槽prop -->
          <CurrentUser>
            <template v-slot:default="defaultProps"
              >{{defaultProps.user.firstName}}</template
            >
      
            <!-- v-slot:type="props" 既取了名字又接收了插槽 prop -->
            <!-- <template v-slot:type="props"> -->
            <!-- 下边是插槽的解构 -->
            <template v-slot:type="{type}">
              <h4>{{type}}</h4>
            </template>
          </CurrentUser>
          <!-- 作用域插槽实现的是父组件在传递插槽的时候可以使用子组件传递的插槽 prop -->
        </div>
      </template>
      
      <script>
        import CurrentUser from "./CurrentUser";
        export default {
          name: "SlotDemo",
          components: { CurrentUser },
        };
      </script>
    • CurrentUser.vue 子组件

      <template>
        <div>
          <slot :type="user.type" name="type" />
          <slot :user="user">{{user.lastName}}</slot>
        </div>
      </template>
      
      <script>
        export default {
          name: "CurrentUser",
          data() {
            return {
              user: { firstName: "大", lastName: "白", type: "管理员" },
            };
          },
        };
      </script>
  • 案例二

    • App.vue 组件

      <template>
        <div id="app">
          <!-- 展示 todo 的部分我想自定义 -->
          <TodoList :todos="todos">
            <template v-slot="{todo}">
              <p>{{todo.text}}</p>
            </template>
          </TodoList>
        </div>
      </template>
      
      <script>
        import TodoList from "./components/SlotDemo1/TodoList";
        export default {
          name: "App",
          components: { TodoList },
          data() {
            return {
              todos: [
                {
                  id: "1",
                  text: 1,
                },
                {
                  id: "2",
                  text: 2,
                },
                {
                  id: "3",
                  text: 3,
                },
              ],
            };
          },
        };
      </script>
    • TodoList.vue 组件

      <template>
        <div>
          todo列表
          <ul>
            <li v-for="todo in todos" :key="todo.id">
              <!-- 将todo传递给父组件 -->
              <slot :todo="todo" />
            </li>
          </ul>
        </div>
      </template>
      
      <script>
        export default {
          name: "TodoList",
          props: ["todos"],
        };
      </script>
  • 案例三

    • ScopeSlotDemo 父组件

      <template>
        <div>
          <h2>作用域插槽</h2>
          <!-- 根据data制作一个列表,循环出来的列表内容要是自定制的 -->
          <Item v-for="ele in list" :key="ele">
            <template v-slot="slotProps">
              <!-- slotProps 接收到的是一个对象,对象里有x这个属性 -->
              <p>{{slotProps.x+ele}}</p>
            </template>
          </Item>
        </div>
      </template>
      
      <script>
        import Item from "./Item";
        export default {
          components: {
            Item,
          },
          name: "ScopeSlotDemo",
          data() {
            return {
              list: [1, 2, 3],
            };
          },
        };
      </script>
    • Item.vue 子组件

      <template>
        <div>
          <!-- 我是列表项 -->
          <slot :x="qianzhui" />
        </div>
      </template>
      
      <script>
        export default {
          name: "Item",
          data() {
            return {
              qianzhui: "哈哈哈",
            };
          },
        };
      </script>

# 表单输入绑定

  • v-model

    <template>
      <div class="form-demo">
        <!-- change 事件失去焦点的时候域的内容发生改变 -->
        <!-- input 域的内容发生改变就触发 -->
        <input type="text" :value="username" @change="handleInput" />
        <!-- vue 提供了一个指定,简化表单的输入绑定 v-model -->
        <!-- 指令修饰符 用法 指令.修饰符-->
        <!-- lazy 将默认的 input事件换成 change事件 -->
        <!-- number 修改的时候只能改成数字 -->
        <!-- trim 去掉左右空白-->
        <input type="text" v-model.trim="password" />
        <input type="checkbox" v-model="agree" />
    
        <!-- 多个复选框 前提是设置 value-->
        <div>
          <h3>你擅长哪些水果?</h3>
          苹果
          <input value="苹果" type="checkbox" v-model="likeFruits" />
          梨
          <input value="梨" type="checkbox" v-model="likeFruits" />
          香蕉
          <input value="香蕉" type="checkbox" v-model="likeFruits" />
        </div>
        <button @click="submit">提交</button>
      </div>
    </template>
    
    <script>
      // 表单输入绑定
      // 必须将输入的内容和组件的 data 绑定,然后使用事件去修改
      export default {
        name: "FormDemo",
        data() {
          return {
            username: "cxh",
            password: "",
            agree: true,
            likeFruits: ["苹果"],
          };
        },
        methods: {
          handleInput(e) {
            this.username = e.target.value;
          },
          submit() {
            console.log(
              this.username,
              this.password,
              this.agree,
              this.likeFruits
            );
          },
        },
      };
    </script>
  • 修改 v-model

    • Parent3.vue

      <template>
        <div>
          <p>父组件内的data count:{{ count }}</p>
          <input type="text" :value="count" @input="handleInput" />
          <Son3 v-model="count" />
          <!-- v-model不能设置表达式,如果要对 v-model的值进行计算的话,
          可以使用计算属性,但是必须搭配计算属性的get和set -->
          <input type="text" v-model="formatPrice" />
          <button @click="handleClick">测试结果</button>
        </div>
      </template>
      
      <script>
        import Son3 from "./Son3";
        export default {
          name: "Parent3",
          components: { Son3 },
          data() {
            return {
              count: 1,
              // price 是后台数据库返回的结果
              price: 7500,
            };
          },
          methods: {
            handleInput(e) {
              this.count = e.target.value;
            },
            handleClick() {
              console.log(this.price);
            },
          },
          computed: {
            formatPrice: {
              get() {
                return (this.price / 100).toFixed(2);
              },
              set(newValue) {
                // console.log(newValue);
                this.price = newValue * 100;
              },
            },
          },
        };
      </script>
      
      <style></style>
    • Son3.vue

      <template>
        <div>
          <p>获取父组件内的data count:{{ value }}</p>
          <button @click="handleClick">使用 v-model 传递的事件</button>
        </div>
      </template>
      
      <script>
        export default {
          name: "Son3",
          props: ["value"],
          created() {
            console.log(this.$emit("handleInput"));
          },
          methods: {
            handleClick() {
              this.$emit("input", 1000);
            },
          },
        };
      </script>
  • 弹窗添加删除案例

    • 父组件

      <template>
        <div id="app">
          <ul>
            <li v-for="goods in goodsList" :key="goods.id">
              <span>{{goods.goodsName}}——${{goods.price}}</span>
              <button @click="handleDel(goods.id)">删除</button>
            </li>
          </ul>
          <dialog
            @close="close_visibility"
            :visibility="visibility"
            title="提示信息"
          >
            <p>是否删除?</p>
            <template #footer>
              <button @click="close_visibility">取消</button>
              <button @click="del">确定</button>
            </template>
          </dialog>
      
          <hr />
      
          <button @click="visibility_other = true">添加</button>
      
          <dialog
            @close="close_visibility_other"
            :visibility="visibility_other"
            title="新增操作"
          >
            <label for>商品名称</label>
            <input type="text" v-model="newGoodsName" />
            <br />
            <label for>商品价格</label>
            <input type="text" v-model="newGoodsPrice" />
      
            <template #footer>
              <button @click="close_visibility_other">取消</button>
              <button @click="add">确定</button>
            </template>
          </dialog>
        </div>
      </template>
      
      <script>
        import Dialog from "./components/DialogDemo/Dialog";
        export default {
          name: "App",
          components: {
            Dialog,
          },
          data() {
            return {
              visibility: false,
              visibility_other: false,
              goodsList: [
                {
                  id: 1,
                  goodsName: "iphone 12 pro max",
                  price: 12000,
                },
                {
                  id: 2,
                  goodsName: "三星 S12 plus",
                  price: 15000,
                },
              ],
              newGoodsName: "",
              newGoodsPrice: "",
            };
          },
          methods: {
            close_visibility() {
              this.visibility = false;
            },
            close_visibility_other() {
              this.visibility_other = false;
            },
            del() {
              this.goodsList.splice(
                this.goodsList.findIndex((item) => item.id === this.delId),
                1
              );
              this.close_visibility();
            },
            handleDel(id) {
              this.visibility = true;
              // 并不需要将 delId 设置为 data,直接设置为全局的变量即可
              this.delId = id;
            },
            add() {
              this.goodsList.push({
                id: new Date().getTime(),
                goodsName: this.newGoodsName,
                price: this.newGoodsPrice,
              });
              this.close_visibility_other();
            },
          },
        };
      </script>
    • 子组件

      <template>
        <div v-show="visibility" class="dialog-wrap">
          <div @click="$emit('close')" class="mask"></div>
      
          <div class="dialog">
            <div class="dialog-header">
              <span>{{title}}</span>
              <span @click="$emit('close')">X</span>
            </div>
            <div class="dialog-body">
              <slot />
            </div>
            <div class="dialog-footer">
              <slot name="footer"></slot>
            </div>
          </div>
        </div>
      </template>
      
      <script>
        export default {
          name: "Dialog",
          props: {
            visibility: {
              type: Boolean, // 值的类型,非必须的
              default: false, // 默认值(默认值是对象类的时候必须使用函数返回该对象),非必须的
            },
            title: {
              type: [Number, String],
              default: "默认的标题",
            },
          },
        };
      </script>

# 数组,对象数据怎么更改才会引起视图更新

已存在的直接修改,不存在的使用直接赋值或者使用$set

<template>
  <div>
    <!-- 了解一下 data 中的数据如果使用对象类型定义的话,什么样的改变才能引起视图的变化 -->
    <h3>data的变化</h3>
    <!-- 数组数据如何产生变化 -->
    <ul>
      <li v-for="(num, index) in arr" :key="index">{{ num }}</li>
    </ul>
    <!-- 数组的一些方法 push,pop,shift,unshift,splice,sort,reverse -->
    <button @click="arr.push(4)">push</button>
    <!-- 直接替换 -->
    <button @click="arr = [1, 2, 3, 4, 5]">替换</button>
    <!-- 对数组修改不能引起视图更新的 -->
    <button @click="arr[1] = 100">利用数组的下标直接修改</button>
    <button @click="arr.length = 1">利用数组的length直接修改</button>
    <!-- 上述不能修改的解决方案 -->
    <button @click="arr.splice(1, 1, 100)">解决下标的修改</button>
    <button @click="arr = arr.slice(0, 1)">解决length的修改</button>
    <!-- 也可以利用 vue 提供的 set 方法解决视图不更新的问题 -->
    <button @click="$set(arr, 1, 100)">set 解决下标的修改</button>
    <button @click="fun">set 解决下标的修改</button>
    <hr />
    <div class="post">
      <h3>{{ post.title }}</h3>
      <span>作者·{{ post.author }}</span>
      <span v-if="post.advertisement">我是广告</span>
    </div>
    <!-- 对象数据如何产生变化 -->
    <!-- 直接对已存在的属性进行修改 -->
    <button @click="post.title = 'vue 基础'">直接通过属性修改标题</button>
    <!-- 直接对对象重新赋值 -->
    <button @click="post = { id: '111', title: 'react', author: '大黑' }">
      直接赋值修改
    </button>
    <!-- 添加一个新的属性,直接新增不会引起视图更新 -->
    <button @click="post.advertisement = true">直接添加广告属性</button>
    <!-- 使用重新赋值的方式新增  -->
    <button @click="post = { ...post, advertisement: true }">
      利用重新赋值添加广告属性
    </button>
    <!-- set 实现对象的属性赋值 -->
    <button @click="$set(post,'advertisement',true)">
      利用set添加广告属性
    </button>
  </div>
</template>

<script>
  export default {
    name: "DataDemo",
    data() {
      return {
        arr: [1, 2, 3],
        post: {
          id: "fdjslfs1231",
          title: "vue 从入门到精通",
          author: "小白",
        },
      };
    },
    methods: {
      fun() {
        this.$set(this.arr, 1, 100);
      },
    },
  };
</script>

# 计算属性 (computed)

在计算属性中不可以有异步操作
计算属性就 是另一种计算之后的 data (data 数据的计算数据), 是谁想用谁用,但是 他只是一个值
计算属性是 基于它们的响应式依赖进行缓存 的 ( 如果属性值是相同的话就不去重新计算 )
只能写函数,而且 必须设置返回值

  • 小案例 (购物车案例)

    <template>
      <div>
        <h3>购物车案例</h3>
        <table>
          <thead>
            <th>产品编号</th>
            <th>产品名称</th>
            <th>购买数量</th>
            <th>产品单价</th>
            <th>产品总价</th>
            <th>操作</th>
          </thead>
          <tbody>
            <tr v-for="product in carts" :key="product.id">
              <td>{{product.id}}</td>
              <td>{{product.name}}</td>
              <td>
                <button @click="sub(product.id)">-</button>
                <!-- 如果 v-model 的值是 prop 那么直接写的就是可以直接对 prop 修改,不推荐 -->
                <!-- 当 prop 是对象类型的时候,子组件可以直接修改,而且修改的时候视图也会更新,不推荐 -->
                <input type="text" v-model.number.lazy="product.counts" />
                <!-- 如果 v-model 的值是 prop 怎么更新 -->
                <!-- 使用 value 展示个数,然后绑定 onChange 事件,事件内传递 id 然后执行父组件的修改函数 -->
                <button @click="add(product.id)">+</button>
              </td>
              <td>¥{{product.price}}</td>
              <td>¥{{product.price* product.counts}}</td>
              <td>
                <button @click="delProduct(product.id)">移除</button>
              </td>
            </tr>
          </tbody>
        </table>
        <div>
          <span>商品总价</span>
          <!-- <span>¥{{getTotal().total}}</span> -->
          <span>¥{{getTotal.total}}</span>
          <span>商品总数</span>
          <!-- <span>{{getTotal().totalNum}}</span> -->
          <span>{{getTotal.totalNum}}</span>
        </div>
      </div>
    </template>
    
    <script>
      export default {
        name: "ShoppingCart",
        data() {
          return {
            carts: [
              { id: 1, name: "iphone5s", price: 4000, counts: 1 },
              { id: 2, name: "iphone5", price: 3000, counts: 1 },
              { id: 3, name: "imac", price: 7000, counts: 1 },
              { id: 4, name: "ipad", price: 6000, counts: 1 },
            ],
          };
        },
        computed: {
          // 只能写函数,而且必须设置返回值
          getTotal() {
            return this.carts.reduce(
              (res, item) => {
                res.total += item.counts * item.price;
                res.totalNum += item.counts;
                return res;
              },
              {
                total: 0,
                totalNum: 0,
              }
            );
          },
        },
        methods: {
          // 删除事件
          delProduct(id) {
            // 删除数组中 跟 id 对应的数据
            this.carts.splice(
              this.carts.findIndex((product) => product.id === id),
              1
            );
          },
          add(id) {
            // 修改数组中 跟 id 对应的数据
            this.carts.find((item) => item.id === id).counts += 1;
          },
          sub(id) {
            // 修改数组中 跟 id 对应的数据
            const currentProduct = this.carts.find((item) => item.id === id);
            if (currentProduct.counts > 1) {
              currentProduct.counts--;
            }
          },
        },
      };
    </script>
  • getter 和 setter

    <template>
      <div>
        <h3>计算属性详细介绍</h3>
        <label for>姓</label>
        <input type="text" v-model="firstName" />
        <br />
        <label>名</label>
        <input type="text" v-model="lastName" />
        <br />
        <!-- 当修改计算属性时,计算的原数据也要跟着改变,那么就必须设置计算属性的 setter -->
        <!-- 其实计算属性默认就是 getter -->
        <label>全名</label>
        <input type="text" v-model="fullName" />
      </div>
    </template>
    
    <script>
      export default {
        name: "ComputedDemo",
        data() {
          return {
            firstName: "张",
            lastName: "三",
          };
        },
        computed: {
          // 计算属性的对象写法
          fullName: {
            get() {
              return this.firstName + " " + this.lastName;
            },
            // 当计算属性被修改的时候触发,执行你想要的执行的操作,该函数接收新的 计算属性的值为参数
            set(newValue) {
              const nameArr = newValue.split(" ");
              this.firstName = nameArr[0];
              this.lastName = nameArr[1];
            },
          },
        },
      };
    </script>

# 侦听器

侦听器的作用是 根据一个 data 的变化去修改另外一个 data 的变化 (newValue 是新的值,oldValue 是原来的值)
计算属性是 根据一个 data 计算出另一个 data
侦听器的侦听对象:data,prop, 路由信息

  • 案例

    <template>
      <div>
        <h3>侦听器</h3>
        <label for>问题</label>
        <input v-model.lazy="question" type="text" />
        <p>{{answer}}</p>
      </div>
    </template>
    
    <script>
      export default {
        name: "WatchDemo",
        data() {
          return {
            question: "",
            answer: "",
          };
        },
        watch: {
          // 监听 question,当question发生改变时,监听事件就会触发,从而可以修改另一个data
          // 写法一:函数语法
          /* question(newValue) {
            setTimeout(() => {
              this.answer = newValue.length > 5 ? (this.answer = "是") : "不是";
            }, 1000);
          } */
          // 写法二:对象语法
          question: {
            handler(newValue) {
              this.answer = "搜索中...";
              // console.log("question改变了就执行");
              setTimeout(() => {
                this.answer = newValue.length > 5 ? (this.answer = "是") : "不是";
              }, 1000);
            },
            // 当监听的数据是对象的时候,想要监听其属性的改变,就需要加deep
            deep: false,
            // 当组件出现的时候,就需要监听数据,更新对应数据,就加immediate
            immediate: false,
          },
        },
      };
    </script>

# 修饰符

<template>
  <div>
    <h3>指令的修饰符</h3>
    <!-- 修饰符是由点开头的指令后缀 -->

    <!-- v-model 指令修饰符 -->
    <!-- 1.lazy 2.trim 3.number -->

    <!-- v-on 指令(事件)修饰符 重要 -->
    <!-- 1.stop 阻止事件冒泡 -->
    <div @click="test" class="box">
      <button @click.stop="stopFun">stop修饰符</button>
    </div>

    <!-- 2.prevent 阻止默认行为 -->
    <a href="#" @click.prevent="test">111</a>
    <!-- 3.capture 将事件在捕获阶段触发,要绑定在外层,用处不大 -->
    <!-- 点里边的按钮,外边的先触发,里边的后触发 -->

    <!-- 4.self 事件只有点击自己本身的时候才会触发 -->
    <!-- 5.once 事件只触发一次 -->

    <!-- v-on 按键修饰符 键盘事件修饰符 -->
    <input v-model="text" type="text" @keydown.13="test" />
    <!-- .+ 按键名 -->

    <!-- 系统修饰符 -->
    <!-- 按住键盘的某个键(ctrl,alt……) -->

    <!-- .exact修饰符 不重要 -->

    <!-- 鼠标修饰符 -->
    <!-- .left .right .middle -->
  </div>
</template>

<script>
  export default {
    name: "ModDemo",
    data() {
      return {
        text: "",
      };
    },
    methods: {
      test() {
        console.log("我是测试事件");
      },
      stopFun() {
        console.log("我是一个事件添加了stop修饰符");
      },
    },
  };
</script>

# 动态组件 (component)

component 标签在 template 中直接使用。必须接收一个 is 属性 (其中 is 后边的必须是 data 中表示标签名的一个变量)
动态组件判断的是标签的名字

  • 案例

    • 父组件 DynamicDemo.vue

      <template>
        <div>
          <h3>动态组件</h3>
          <div>
            <button @click="currentType='Home'">Home</button>
            <button @click="currentType='Posts'">Posts</button>
            <button @click="currentType='About'">About</button>
          </div>
      
          <!-- 必须接收一个 is 属性 -->
          <component :is="currentType"></component>
        </div>
      </template>
      
      <script>
        import Home from "./Home";
        import Posts from "./Posts";
        import About from "./About";
        export default {
          name: "DynamicDemo",
          components: {
            Home,
            Posts,
            About,
          },
          data() {
            return {
              currentType: "About",
            };
          },
        };
      </script>
    • 子组件 Home.vue Posts.vue About.vue

      <template>
        <div>Home component</div>
        <!-- Posts组件 改成 Posts component
        About组件 改成 About component -->
      </template>
      
      <script>
        export default {
          name: "Home",
        };
      </script>
  • keep-alive
    让组件能保持上次的操作状态 (可以实现状态的保存)
    include - 字符串或正则表达式。 只有名称匹配的组件会被缓存 。
    exclude - 字符串或正则表达式。 任何名称匹配的组件都不会被缓存 。
    max - 数字。 最多可以缓存多少组件实例 。
    max 只是限制 keep-alive 下边能有几个实例

    • KeepAliveDemo.vue 父组件

      <template>
        <div>
          <h3>在动态组件上使用 keep-alive</h3>
          <div>
            <span @click="currentPage='Home'">Home</span>
            <span>丨</span>
            <span @click="currentPage='Posts'">Posts</span>
          </div>
          <!-- <keep-alive include="Posts" :max="2"> -->
          <!-- 下边的也可以 -->
          <keep-alive :include="['Posts','Home']" :max="2">
            <component :is="currentPage"></component>
          </keep-alive>
        </div>
      </template>
      
      <script>
        import Home from "./Home";
        import Posts from "./Posts";
        export default {
          name: "KeepAliveDemo",
          components: { Home, Posts },
          data() {
            return {
              currentPage: "Home",
            };
          },
        };
      </script>
    • Home.vue 组件

      <template>
        <div>
          Home页
          <input type="text" v-model="text" />
        </div>
      </template>
      
      <script>
        export default {
          name: "Home",
          data() {
            return {
              text: "",
            };
          },
        };
      </script>
    • Posts.vue 组件

      <template>
        <div>
          <nav>
            <ul>
              <li
                :class="type === 'frontend' && 'active'"
                @click="type='frontend'"
              >
                前端
              </li>
              <li :class="type === 'backend' && 'active'" @click="type='backend'">
                后端
              </li>
              <li
                :class="type === 'fullstack' && 'active'"
                @click="type='fullstack'"
              >
                全栈
              </li>
            </ul>
          </nav>
        </div>
      </template>
      
      <script>
        export default {
          name: "Posts",
          data() {
            return {
              // frontend 前端 backend 后端 fullstack 全栈
              type: "frontend",
            };
          },
        };
      </script>

# 全局组件注册

Vue.component()

  • 案例

    • plugins.js 文件

      // 使用 Vue.component() 方法实现组件的全局注册
      import Vue from "vue";
      import GlobalBtn from "./components/GlobalBtn";
      Vue.component("GlobalBtn", GlobalBtn);
      // 第一个是注册的名字,第二个是注册的组件
    • main.js 文件

      import "./plugins";
    • GlobalBtn.vue 文件

      <template>
        <button>{{text}}</button>
      </template>
      
      <script>
        export default {
          props: {
            text: {
              default: "我是全局组件",
              type: String,
            },
          },
        };
      </script>
    • Home.vue 文件

      <template>
        <div>
          Home component
          <GlobalBtn text="home按钮" />
        </div>
      </template>
      
      <script>
        export default {
          name: "Home",
        };
      </script>

# 自动导入全局注册化组件

(https://cn.vuejs.org/v2/guide/components-registration.html# 基础组件的自动化全局注册)
需要下载 lodash
不用一个一个的导入了,自动帮你导入进去之后就可以直接使用

  • 案例

    • plugins.js 文件

      import Vue from "vue";
      import upperFirst from "lodash/upperFirst";
      import camelCase from "lodash/camelCase";
      
      // 自动导入所有满足条件的组件
      const requireComponent = require.context(
        // 其组件目录的相对路径
        "./components/GlobalComponents/", // 这里是重点
        // 是否查询其子目录
        false,
        // 匹配基础组件文件名的正则表达式
        /Global[A-Z]\w*\.(vue|js)$/ // 这里是重点
      );
      // console.log(requireComponent.keys());
      
      // requireComponent.keys() 就是导入的所有组件的路径组成的数组
      requireComponent.keys().forEach((fileName) => {
        // 获取组件配置(导入对应的组件,组件有可能事默认导出也有可能是命名导出)
        const componentConfig = requireComponent(fileName);
      
        // 获取组件的 PascalCase 命名
        // ./GlobalBtn.vue 这是 fileName
        // ./global-btn.vue ---> GlobalBtn
        const componentName = upperFirst(
          camelCase(
            // 获取和目录深度无关的文件名
            fileName
              .split("/")
              .pop()
              .replace(/\.\w+$/, "")
          )
        );
      
        // 全局注册组件
        Vue.component(
          componentName,
          // 如果这个组件选项是通过 `export default` 导出的,
          // 那么就会优先使用 `.default`,
          // 否则回退到使用模块的根。
          componentConfig.default || componentConfig
        );
      });
    • main.js 文件 这里要引入 plugins.js 文件

      import "./plugins";
    • App.vue 文件

      <template>
        <GlobalH title="啊哈哈哈哈" />
        <GlobalBox1 />
      </template>

文章作者: Mrr-cxh
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Mrr-cxh !
赏
 上一篇
Vue 3.0 Vue 3.0
Vue 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。
2021-05-08
下一篇 
Vue 1.0 Vue 1.0
Vue 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。
2021-04-23
  目录