Vue 3.0


# todo 大案例

  • TodoWrap.vue 父组件

    <template>
      <div class="todo-wrap">
        <h1>todos</h1>
        <TodoForm
          :isAllTodoCompleted="isAllTodoCompleted"
          @changeAllTodo="changeAllTodo"
          @addTodo="addTodo"
          :len="todos.length"
        />
        <!-- 自定义事件只能传递一次,所以这里使用了prop方式传递 -->
        <!-- 第一次用prop传递给儿子,第二次在儿子用自定义事件传递给孙子 -->
        <div v-show="todos.length">
          <TodoList
            :todos="showTodos"
            :completedTodo="completedTodo"
            :delTodo="delTodo"
            :changeTodoText="changeTodoText"
          />
    
          <!-- .sync 缩写 -->
          <!-- .sync 不仅传递了自定义事件,还传递了对应的prop -->
          <TodoFooter
            :type.sync="type"
            @delAllCompleted="delAllCompleted"
            :activeTodoNums="activeTodoNums"
            :completedTodoNums="completedTodoNums"
          />
        </div>
      </div>
    </template>
    
    <script>
      import TodoForm from "./TodoForm";
      import TodoList from "./TodoList";
      import TodoFooter from "./TodoFooter";
      export default {
        name: "TodoWrap",
        components: { TodoForm, TodoList, TodoFooter },
        data() {
          return {
            todos: [{ id: "7t7t7d", text: "666", isCompleted: false }],
            type: "all",
            editTodoId: "",
          };
        },
        computed: {
          // 判断 所有的 isCompleted 是否为被选中
          isAllTodoCompleted() {
            // 让箭头为灰色
            // 当数组为空的时候,every 的结果是true
            return this.todos.length
              ? this.todos.every((todo) => todo.isCompleted)
              : false;
          },
          showTodos() {
            const { type, todos } = this;
            return this.filterTodoByType(type, todos);
          },
          activeTodoNums() {
            return this.filterTodoByType("active", this.todos).length;
          },
          completedTodoNums() {
            return this.filterTodoByType("completed", this.todos).length;
          },
        },
        methods: {
          // 通过类别筛选数组的功能函数
          filterTodoByType(type, todos) {
            return todos.filter((todo) =>
              type === "all"
                ? true
                : type === "active"
                ? !todo.isCompleted
                : todo.isCompleted && type === "completed"
            );
          },
          // 删除 todo 函数
          delTodo(id) {
            const delIndex = this.todos.findIndex((todo) => todo.id === id);
            if (delIndex > -1) {
              this.todos.splice(delIndex, 1);
            }
          },
          // 修改 todo 的 isCompleted 属性
          completedTodo(id) {
            const currentTodo = this.todos.find((todo) => todo.id === id);
            if (currentTodo) {
              currentTodo.isCompleted = !currentTodo.isCompleted;
            }
          },
          // 修改所有 todo 的 isCompleted
          changeAllTodo(status) {
            this.todos.forEach((item) => {
              if (item.isCompleted !== status) {
                item.isCompleted = status;
              }
            });
          },
          // 输入框内容添加到数组中
          addTodo(text) {
            this.todos.push({
              id: new Date().getTime(),
              text,
              isCompleted: false,
            });
          },
          // 删除所有的已完成
          delAllCompleted() {
            // console.log(id);
            this.todos = this.todos.filter((todo) => !todo.isCompleted);
          },
          // 修改对应的 todo 内容
          changeTodoText(id, text) {
            const currentTodo = this.todos.find((todo) => todo.id === id);
            if (currentTodo) {
              currentTodo.text = text;
            }
          },
        },
      };
    </script>
  • TodoForm.vue 子组件

    <template>
      <div class="todo-form">
        <i
          v-show="len"
          :class="['iconfont', 'icon-xiala' ,'icon',{'all-completed':isAllTodoCompleted}]"
          @click="$emit('changeAllTodo',!isAllTodoCompleted)"
        ></i>
        <input
          type="text"
          placeholder="输入内容"
          v-model.trim="value"
          @keydown.enter="handleEnter"
        />
      </div>
    </template>
    
    <script>
      export default {
        name: "TodoForm",
        props: ["isAllTodoCompleted", "len"],
        data() {
          return {
            value: "",
          };
        },
        methods: {
          handleEnter() {
            const { value } = this;
            if (value) {
              this.$emit("addTodo", value);
              this.value = "";
            }
          },
        },
      };
    </script>
  • TodoList.vue 子组件

    <template>
      <div class="todo-list">
        <!-- 循环的时候 todo 指的是具体的一项,我们需要其所有内容全部传递给 TodoItem 组件 -->
        <!-- 所以选择了传递整个对象 :todo="todo",也可以直接将整个对象下的所有属性一次性传递 v-bind="todo" -->
        <TodoItem
          v-for="todo in todos"
          :key="todo.id"
          v-bind="todo"
          @delTodo="delTodo"
          @completedTodo="completedTodo"
          @changeTodoText="changeTodoText"
        />
      </div>
    </template>
    
    <script>
      import TodoItem from "./TodoItem";
      export default {
        name: "TodoList",
        components: { TodoItem },
        props: ["todos", "delTodo", "completedTodo", "changeTodoText"],
      };
    </script>
  • TodoItem.vue 孙子组件

    <template>
      <div class="todo-item">
        <div class="checkbox">
          <input
            type="checkbox"
            :id="id"
            :checked="isCompleted"
            @change="$emit('completedTodo', id)"
          />
          <label :for="id"></label>
        </div>
        <span
          :class="['todo-text', { completed: isCompleted }]"
          @dblclick="dblClick"
          v-show="!isEditing"
          >{{ text }}</span
        >
        <!-- autofocus html自动获取焦点属性 -->
        <input
          type="text"
          class="edit-input"
          :value="text"
          v-show="isEditing"
          @blur="handleBlur(id,$event.target.value)"
          ref="inputEle"
        />
        <!-- 上面失去焦点事件内想要将输入框的内容当作参数传递下去 -->
        <!-- vue 的事件函数内 直接可以使用 $event 代表事件对象 -->
        <span class="del" @click="$emit('delTodo', id)">X</span>
      </div>
    </template>
    
    <script>
      export default {
        name: "TodoItem",
        props: ["id", "text", "isCompleted"],
        // 因为这里采用了传递属性的方式,所以上边的属性都没有了todo.id ,直接变成了id
        data() {
          return {
            isEditing: false,
          };
        },
        methods: {
          dblClick() {
            this.isEditing = true;
            // 使用 focus 方法 让 input 获取焦点
            // 直接使用 focus 方法获得焦点不成功
            // 因为 focus 执行的时候,页面中的 input 并没有出现(focus比先出现先执行,获得焦点在出现之前执行的)
            // 当直接修改了数据之后想要获取对应的更新之后的dom节点,需要借助 $nextTick 方法
            this.$nextTick(() => {
              // document.querySelector(".edit-input").focus(); //这个不保险,推荐使用ref
              this.$refs.inputEle.focus();
              // document 方式获取真实 dom 节点,需要替换成 ref
              // vue 中提供了 获取真实 dom 节点的方式 就是 ref
              // 当给普通的 html 添加 ref 属性时,ref 指的就是 html 节点的真实 dom,
              // 当 ref 属性添加到组件上时 ref 指的就是组件本身
              // 获取 ref 就是使用组件 this.$refs.xx 获取
              // 所以在父组件中给子组件添加 ref 属性,可以在父组件中获取子组件,那么子组件内的所有内容父组件都可以获取了
            });
          },
          handleBlur(id, text) {
            this.isEditing = false;
            this.$emit("changeTodoText", id, text);
          },
        },
      };
    </script>
  • TodoFooter.vue 子组件

    <template>
      <div class="todo-footer">
        <span class="left"
          >{{activeTodoNums}} item{{activeTodoNums !== 1 ?'s':''}} left</span
        >
        <div class="btns">
          <!-- changeType 换成了 update:type  -->
          <button
            :class="type==='all'&&'active'"
            @click="$emit('update:type', 'all')"
          >
            all
          </button>
          <button
            :class="type==='active'&&'active'"
            @click="$emit('update:type', 'active')"
          >
            active
          </button>
          <button
            :class="type==='completed'&&'active'"
            @click="$emit('update:type', 'completed')"
          >
            completed
          </button>
        </div>
        <button
          class="right"
          @click="$emit('delAllCompleted')"
          v-show="completedTodoNums"
        >
          clear completed
        </button>
      </div>
    </template>
    
    <script>
      export default {
        name: "TodoFooter",
        props: ["type", "activeTodoNums", "completedTodoNums"],
      };
    </script>

# .sync 修饰符

.sync 修饰符的用法 不需要在父组件定义修改的方法,一般只用在非引用类型的 data 上

  • 父组件 TodoWrap.vue 文件

    <template>
      <div class="todo-wrap">
        <!-- <TodoFooter @changeType="changeType" /> -->
    
        <!-- 这个自定义事件的名称是 update:type 传递的函数其实是 function($event){type=$event} -->
        <!-- 使用 .sync 不用传递函数,下边已注释 -->
        <!-- <TodoFooter @update:type="type = $event" /> -->
        <!-- changeType 换成了 update:type  -->
    
        <!-- .sync 缩写 -->
        <TodoFooter :type.sync="type" />
      </div>
    </template>
    
    <script>
      export default {
        name: "TodoWrap",
        components: { TodoFooter },
        data() {
          return {
            type: "all",
            // 这个 data 需要子组件修改,一般做法是在父组件定义好的方法,然后传递给子组件执行
            // 也可以使用 自定义事件的 .sync 修饰符实现,一般只用在非引用数据类型的data上
            // .sync 修饰符的用法不需要在父组件定义修改的方法
          };
        },
        methods: {
          /* changeType(newType) {
            this.type = newType;
          }, */
        },
      };
    </script>
  • 子组件 TodoFooter.vue 文件

    <template>
      <div class="todo-footer">
        <span class="left">{{2}} items left</span>
        <div class="btns">
          <!-- <button @click="$emit('changeType','all')">all</button>
          <button @click="$emit('changeType','active')">active</button>
          <button @click="$emit('changeType','completed')">completed</button> -->
    
          <!-- changeType 换成了 update:type  -->
          <button @click="$emit('update:type','all')">all</button>
          <button @click="$emit('update:type','active')">active</button>
          <button @click="$emit('update:type','completed')">completed</button>
        </div>
        <button class="right">clear completed</button>
      </div>
    </template>

# table 大案例

  • App.vue 文件

    <template>
      <div id="app">
        <TableFilter
          :filterTitleStr.sync="filterTitleStr"
          @changeShow="changeShow"
        />
    
        <table
          :postList="showPostList"
          :delPost="delPost"
          :changeShow="changeShow"
          :changeEditId="changeEditId"
          :filterTitleStr="filterTitleStr"
        />
    
        <PostDialog
          v-if="show"
          :show="show"
          @changeShow="changeShow"
          @onOk="onOk"
          :getPostByEditId="getPostByEditId"
          @changeEditId="changeEditId"
        />
      </div>
    </template>
    
    <script>
      import TableFilter from "./components/TableFilter";
      import Table from "./components/Table";
      import PostDialog from "./components/PostDialog";
      import shortid from "shortid";
      export default {
        name: "App",
        components: { TableFilter, Table, PostDialog },
        data() {
          return {
            postList: [
              {
                id: 1,
                time: "2275-11-26",
                title: "Ytifwsjgg Qqqy Dskb Lstgceu Knrd Msev",
                country: "USA",
                author: "Sarah",
                reads: "3276",
              },
              {
                id: 2,
                time: "2216-03-28",
                title: "Lkzv Dmnrpoe Fgj Pueqgbp Doj",
                country: "Japan",
                author: "Susan",
                reads: "3485",
              },
            ],
            filterTitleStr: "",
            show: false,
            editId: "",
          };
        },
        computed: {
          // 展示的内容
          showPostList() {
            return this.postList.filter((post) =>
              post.title.includes(this.filterTitleStr)
            );
          },
          // 通过editId找到对应的内容
          getPostByEditId() {
            return this.postList.find((post) => post.id === this.editId);
          },
          // 通过有无editId来判断是添加操作还是修改操作
          onOk() {
            return this.editId === "" ? this.addPost : this.changePost;
          },
        },
        methods: {
          // 删除对应的内容
          delPost(id) {
            this.postList = this.postList.filter((post) => post.id !== id);
          },
          // 改变对话框的出现状态
          changeShow() {
            this.show = !this.show;
          },
          // 添加内容
          addPost(newPost) {
            this.postList.push({ ...newPost, id: shortid() });
          },
          // 改变EditId
          changeEditId(id) {
            this.editId = id;
          },
          // 对话框中修改对应的内容
          changePost(newPost) {
            this.postList.splice(
              this.postList.findIndex((post) => post.id === this.editId),
              1,
              { ...newPost, id: this.editId }
            );
          },
        },
      };
    </script>
  • TableFilter.vue 文件

    <template>
      <div class="table-filter">
        <!-- 表格筛选 -->
        <input type="text" v-model.trim.lazy="searchValue" />
        <button class="search" @click="search">搜索</button>
        <button class="add" @click="$emit('changeShow')">添加</button>
      </div>
    </template>
    
    <script>
      export default {
        name: "TableFilter",
        data() {
          return {
            searchValue: "",
          };
        },
        methods: {
          search() {
            const { searchValue } = this;
            if (searchValue) {
              this.$emit("update:filterTitleStr", searchValue);
            }
          },
        },
      };
    </script>
  • PostDialog.vue 文件

    <template>
      <div class="dialog-wrap">
        <div class="dialog-mask"></div>
        <div class="dialog">
          <h3>标题</h3>
          <div>
            <label for>时间</label>
            <input v-model="post.time" type="text" />
          </div>
          <div>
            <label for>标题</label>
            <input v-model="post.title" type="text" />
          </div>
          <div>
            <label for>阅读数</label>
            <input v-model="post.reads" type="text" />
          </div>
          <div>
            <label for>作者</label>
            <input v-model="post.author" type="text" />
          </div>
          <div>
            <button @click="onOk">确定</button>
            <button @click="onCancle">取消</button>
          </div>
        </div>
      </div>
    </template>
    
    <script>
      export default {
        name: "PostDialog",
        props: ["getPostByEditId"],
        data() {
          return {
            post: {
              time: "",
              title: "",
              country: "China",
              author: "",
              reads: 0,
            },
          };
        },
        created() {
          if (this.getPostByEditId) {
            const post = { ...this.getPostByEditId };
            // 删除属性
            delete post.id;
            this.post = post;
          }
        },
        methods: {
          onOk() {
            // 应该判断所有输入是否全部输入了然后再执行添加操作
            // 下边执行的onOk是父组件中传递时定义的名字
            this.$emit("onOk", this.post);
            // 关闭弹窗
            this.$emit("changeShow");
            // 清空操作
            this.post = {
              time: "",
              title: "",
              country: "China",
              author: "",
              reads: 0,
            };
            this.$emit("changeEditId", "");
          },
          onCancle() {
            this.$emit("changeShow");
            this.$emit("changeEditId", "");
          },
        },
      };
    </script>
  • Table.vue 文件

    <template>
      <table>
        <thead>
          <th>序号</th>
          <th>时间</th>
          <th>标题</th>
          <th>作者</th>
          <th>阅读数</th>
          <th>操作</th>
        </thead>
        <tbody>
          <!-- 一次性将对象下的所有属性全部传递 -->
          <TrItem
            v-for="post in postList"
            :key="post.id"
            v-bind="post"
            @delPost="delPost"
            @changeShow="changeShow"
            @changeEditId="changeEditId"
            :filterTitleStr="filterTitleStr"
          />
        </tbody>
      </table>
    </template>
    
    <script>
      import TrItem from "./TrItem";
      export default {
        name: "Table",
        components: { TrItem },
        props: [
          "postList",
          "delPost",
          "changeShow",
          "changeEditId",
          "filterTitleStr",
        ],
      };
    </script>
  • TrItem.vue 文件

    <template>
      <tr>
        <td>{{ id }}</td>
        <td>{{ time }}</td>
        <td>
          <span
            v-html="titleHasKeyStr"
            v-if="title.includes(filterTitleStr)&&filterTitleStr!==''"
          ></span>
          <span v-else>{{ title }}</span>
          <button>{{ country }}</button>
        </td>
        <td>{{ author }}</td>
        <td>{{ reads }}</td>
        <td>
          <button @click="editFun(id)">编辑</button>
          <button @click="$emit('delPost', id)">删除</button>
        </td>
      </tr>
    </template>
    
    <script>
      export default {
        name: "TrItem",
        props: [
          "id",
          "time",
          "title",
          "country",
          "author",
          "reads",
          "filterTitleStr",
        ],
        methods: {
          editFun(id) {
            // 展示对话框
            this.$emit("changeShow");
            // 把id传过去,通过id找到对应的内容
            this.$emit("changeEditId", id);
          },
        },
        computed: {
          titleHasKeyStr() {
            const { title, filterTitleStr } = this;
            const ind = title.indexOf(filterTitleStr);
            return (
              title.slice(0, ind) +
              "<span class='key-str'>" +
              filterTitleStr +
              "</span>" +
              title.slice(ind + filterTitleStr.length)
            );
          },
        },
      };
    </script>

# vue 的 transition 过渡

  • 有六个 clss 状态

    • 进入 出现 enter

    • xx-enter :定义进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一帧移除。

    • xx-enter-active :定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡 / 动画完成之后移除。这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数。

    • xx-enter-to : 版及以上定义进入过渡的结束状态。在元素被插入之后下一帧生效 (与此同时 xx-enter 被移除),在过渡 / 动画完成之后移除。

    • 离开 消失 leave

    • xx-leave :定义离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除。

    • xx-leave-active :定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡 / 动画完成之后移除。这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数。

    • xx-leave-to :2.1.8 版及以上定义离开过渡的结束状态。在离开过渡被触发之后下一帧生效 (与此同时 xx-leave 被删除),在过渡 / 动画完成之后移除。

  • 简单案例

    <template>
      <div>
        <h3>vue 的 transition 组件实现过渡</h3>
        <button @click="show1=!show1">测试</button>
        <br />
        <!-- 使用 transition 包裹需要执行过滤效果的元素,并且需要给 transition 组件传递一个 name 属性,
        name 属性的属性值,是要和对应的 class 挂钩的 -->
        <transition name="fade">
          <span class="txt" v-show="show1">hello</span>
        </transition>
      </div>
    </template>
    <style>
      .box {
        width: 100px;
        height: 100px;
        background-color: #ccc;
        transition: opacity 1s;
      }
      .fadeOut {
        opacity: 0;
      }
      /*渐入渐出效果*/
      /* .fade-enter-active,
      .fade-leave-active {
        transition: opacity 0.5s;
      }
      .fade-enter,
      .fade-leave-to {
        opacity: 0;
      } */
      /*左侧进入右侧离开*/
      .txt {
        display: block;
        width: 100px;
        text-align: center;
        border: 1px solid #ccc;
        margin-left: 200px;
      }
      .fade-enter {
        transform: translateX(-100px);
        opacity: 0;
      }
      .fade-enter-active,
      .fade-leave-active {
        transition: all 0.5s;
      }
      .fade-leave-to {
        transform: translateX(100px);
        opacity: 0;
      }
    </style>

# vue 的 动画

还是要理解 class 类名

  • 简单案例

    在 index.html 中引入,这里引入的三版本,四版本的语法变成了 animate__animated animate__flipInY (动画名)

    <link
      href="https://cdn.jsdelivr.net/npm/animate.css@3.5.1"
      rel="stylesheet"
    />
    <template>
      <div>
        <button @click="show2=!show2">按钮</button>
        <transition name="xxx">
          <p v-show="show2">
            Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris
            facilisis enim libero, at lacinia diam fermentum id. Pellentesque
            habitant morbi tristique senectus et netus.
          </p>
        </transition>
    
        <hr />
        <button @click="show3=!show3">按钮</button>
        <!-- 自定义类名,主要注意的是 enter-active-class,leave-active-class 后面的 class 值就是执行动画的类名 -->
        <!-- 注意在 index.html中引入的版本号 -->
        <transition
          appear
          enter-active-class="animated tada"
          leave-active-class="animated flipOutX"
        >
          <p v-show="show3">Lorem ipsum dolor sit amet</p>
        </transition>
    
        <hr />
        <!-- 当相同的元素之间执行动画切换的话,需要添加不同的 key 值 -->
        <!-- <transition name="fade">
          <button @click="isEditing=false" v-if="isEditing" key="save">
            Save
          </button>
          <button @click="isEditing=true" v-else key="edit">Edit</button>
        </transition> -->
        <!-- 上边的简写,mode 是模式 -->
        <transition name="fade" mode="out-in">
          <button :key="isEditing" @click="isEditing=!isEditing">
            {{ isEditing ? 'Save' : 'Edit' }}
          </button>
        </transition>
      </div>
    </template>
    <script>
      export default {
        name: "VueTransiton",
        data() {
          return {
            show2: true,
            show3: true,
            isEditing: false,
          };
        },
      };
    </script>
    
    <style>
      .box {
        width: 100px;
        height: 100px;
        background-color: #ccc;
        transition: opacity 1s;
      }
      /* 文字消失的过程中 */
      .xxx-leave-active {
        animation: bounce 0.5s linear;
      }
      /* 这里和上边的效果相反 */
      /* .xxx-enter-active {
        animation: bounce 0.5s linear reverse;
      } */
      .xxx-enter-active {
        animation: fadeRight 0.5s linear;
      }
      @keyframes bounce {
        0% {
          transform: scale(1);
        }
        50% {
          transform: scale(1.5);
        }
        100% {
          transform: scale(0);
        }
      }
      @keyframes fadeRight {
        0% {
          transform: translateX(300px);
          opacity: 0;
        }
        50% {
          transform: translateX(50px);
          opacity: 0.5;
        }
        100% {
          transform: translateX(0px);
          opacity: 1;
        }
      }
    </style>
  • 初始渲染的过渡 (appear 属性) https://cn.vuejs.org/v2/guide/transitions.html#%E5%88%9D%E5%A7%8B%E6%B8%B2%E6%9F%93%E7%9A%84%E8%BF%87%E6%B8%A1

# 列表过渡

  • 简单案例

    <div id="list-complete-demo" class="demo">
      <button @click="shuffle">Shuffle</button>
      <button @click="add">Add</button>
      <button @click="remove">Remove</button>
      <transition-group name="list-complete" tag="p">
        <span v-for="item in items" v-bind:key="item" class="list-complete-item"
          >{{ item }}</span
        >
      </transition-group>
    </div>
    
    <script>
      // import _ from "lodash";
      import shuffle from "lodash/shuffle";
      export default {
        name: "VueTransiton",
        data() {
          return {
            items: [1, 2, 3, 4, 5, 6, 7, 8, 9],
          };
        },
        methods: {
          add() {
            // 数组中求最大数
            const max = Math.max(...this.items);
            this.items.push(max + 1);
          },
          remove() {
            // 获取一个数组中的随机元素
            const random = Math.floor(Math.random() * (this.items.length + 1));
            this.items.splice(random, 1);
          },
          shuffle() {
            // 随机打乱数组
            /* const shuffleItems = [];
            const copyItems = [...this.items];
            const num = copyItems.length;
            for (let index = 0; index < num; index++) {
              const random = Math.floor(Math.random() * copyItems.length);
              const element = copyItems[random];
              copyItems.splice(random, 1);
              shuffleItems.push(element);
            }
            this.items = shuffleItems; */
            // this.items = _.shuffle(this.items);
            this.items = shuffle(this.items);
          },
        },
      };
    </script>

# 可复用的过渡

  • 简单案例

    • VueTransiton.vue

      <button @click="show4 = !show4">按钮</button>
      <SpecialTransition>
        <p v-show="show4">sf;skfsfsjlfs</p>
        <h3 v-show="show4">sf;skfsfsjlfs</h3>
      </SpecialTransition>
      <script>
        import SpecialTransition from "./SpecialTransition";
        export default {
          name: "VueTransiton",
          components: {
            SpecialTransition,
          },
          data() {
            return {
              show4: true,
            };
          },
        };
      </script>
    • SpecialTransition.vue

      <template>
        <!-- 就是一个 transition 动画组件 -->
        <transition name="fade1">
          <slot />
        </transition>
      </template>
      
      <script>
        export default {
          name: "SpecialTransition",
        };
      </script>
      
      <style scoped>
        .fade1-enter-active,
        .fade1-leave-active {
          transition: opacity 0.5s;
        }
        .fade1-enter,
        .fade1-leave-to {
          opacity: 0;
        }
      </style>

# 动态过渡

默认的过渡效果和动画效果是定死的,只能去修改 name,为了方便使用了 transition 的 js 钩子,通过钩子获取 dom 节点,然后修改 dom 节点的样式

  • 简单案例

    <button @click="show3 = !show3">按钮</button>
    <!-- 自定义类名,主要注意的是 enter-active-class,leave-active-class 后面的 class 值就是执行动画的类名 -->
    出现的时间:<input type="number" v-model.lazy="inTime" /> 消失的时间:<input
      type="number"
      v-model.lazy="outTime"
    />
    <transition
      appear
      @before-enter="show3BeforeEnter"
      @before-leave="show3BeforeLeave"
      enter-active-class="animated tada"
      leave-active-class="animated flipOutX"
    >
      <!-- 这里用的是js钩子before-enter -->
      <div v-show="show3">
        <!-- 动态控制出现和消失的动画持续时间 -->
        <!-- 这个 p 因为使用了animate.css动画库,默认时间是1s -->
        <p>Lorem ipsum dolor sit amet</p>
      </div>
    </transition>
    <script>
      export default {
        data() {
          return {
            show3: true,
            inTime: 1,
            outTime: 1,
          };
        },
        methods: {
          show3BeforeEnter(el) {
            console.log("出现动画执行之前触发", el);
            // el 获取的是原生 dom 节点
            // 下边改的是行内样式,所以要有show3BeforeLeave来改变样式,否则消失的时间也会按照inTime来
            el.style.animationDuration = this.inTime + "s";
          },
          show3BeforeLeave(el) {
            console.log("离开动画执行之前触发", el);
            el.style.animationDuration = this.outTime + "s";
          },
        },
      };
    </script>

# 状态过渡

  • 简单案例

    <h3>状态过渡(非进入离开的动效,比如大小颜色等等)</h3>
    <input type="text" v-model.lazy.number="num" />
    <!-- 当失去焦点的时候,数字展示依次递增或递减到目标数字 -->
    <p>{{ showNum }}</p>
    
    <script>
      export default {
        name: "VueTransiton",
        data() {
          return {
            num: 0,
            showNum: 0,
          };
        },
        watch: {
          num: {
            handler(newValue) {
              const run = setInterval(() => {
                if (this.showNum !== newValue) {
                  this.showNum++;
                } else {
                  clearInterval(run);
                }
              }, 50);
            },
          },
        },
      };
    </script>

# 过滤器

过滤器可直接在 template 中使用,遵循模板语法

  • 简单案例

    <template>
      <div>
        <h3>vue 中过滤器的使用</h3>
        <input type="text" v-model="message" />
        <div>过滤器结果{{ message | capitalize }}</div>
        <!-- message是实参 capitalize是过滤器函数-->
        <!-- <div>计算属性结果:{{ capitalizeMessage(message) }}</div> -->
        <div>计算属性结果:{{ capitalizeMessage }}</div>
      </div>
    </template>
    
    <script>
      export default {
        name: "FilterDemo",
        data() {
          return {
            message: "hello",
          };
        },
        computed: {
          // 要实现计算属性传参需要给计算属性返回函数类型,一般不使用计算属性返回一个函数
          capitalizeMessage() {
            // return (str) => str.charAt(0).toUpperCase() + str.slice(1);
            return this.message.charAt(0).toUpperCase() + this.message.slice(1);
          },
        },
        // 过滤器和计算属性写法很像,区别是前者的函数可以接收参数
        filters: {
          // 字符串首字母大写的过滤器
          capitalize(value) {
            if (!value) return "";
            value = value.toString();
            return value.charAt(0).toUpperCase() + value.slice(1);
          },
        },
      };
    </script>
  • 全局过滤器

    • main.js 文件

      Vue.filter("capitalize", function (value) {
        if (!value) return "";
        value = value.toString();
        return value.charAt(0).toUpperCase() + value.slice(1);
      });
    • FilterDemo.vue 组件 因为是全局的,所以可以直接使用

      <template>
        <div>
          <h3>vue 中过滤器的使用</h3>
          <input type="text" v-model="message" />
          <div>过滤器结果{{ message | capitalize }}</div>
          <!-- message是实参 capitalize是过滤器函数-->
          <div>计算属性结果:{{ capitalizeMessage }}</div>
        </div>
      </template>
      
      <script>
        export default {
          name: "FilterDemo",
          data() {
            return {
              message: "hello",
            };
          },
          computed: {
            // 要实现计算属性传参需要给计算属性返回函数类型,一般不使用计算属性返回一个函数
            capitalizeMessage() {
              // return (str) => str.charAt(0).toUpperCase() + str.slice(1);
              return this.message.charAt(0).toUpperCase() + this.message.slice(1);
            },
          },
        };
      </script>
  • 搜索案例
    在 vue2 中 v-html 内默认不能使用过滤器,需要使用 $options.filters.过滤器名称 获取过滤器函数然后直接使用

    <template>
      <div>
        <input type="text" v-model.lazy="searchTxt" />
        <div>
          <p v-for="todo in showTodos" :key="todo.id">
            <!-- 如果 todo 的内容部分包含了 searchTxt,那么对应的关键字变色 -->
            <span v-if="!searchTxt">{{ todo.text }}</span>
            <!-- 在 vue2 中 v-html 内默认不能使用过滤器,需要使用 $options.filters.过滤器名称 获取过滤器函数然后直接使用 -->
            <span
              v-else
              v-html="$options.filters.getStr(todo.text, searchTxt)"
            ></span>
          </p>
        </div>
      </div>
    </template>
    
    <script>
      export default {
        name: "FilterDemo",
        data() {
          return {
            todos: [
              { id: "111", text: "fsjklfsn" },
              { id: "222", text: "nmvn,xcmnv" },
              { id: "333", text: "equwoiuo" },
            ],
            searchTxt: "",
          };
        },
        computed: {
          showTodos() {
            return this.todos.filter((todo) =>
              todo.text.includes(this.searchTxt)
            );
          },
        },
        filters: {
          getStr: function (text, searchTxt) {
            return `${text.slice(
              0,
              text.indexOf(searchTxt)
            )}<span class='color'>${searchTxt}</span>${text.slice(
              text.indexOf(searchTxt) + searchTxt.length
            )}`;
          },
        },
      };
    </script>

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