vue3+typescript【2】

method中this的指向

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

快捷模板

文件->首选项->配置用户代码->

html.json:

{
    "Print to vue": {
		"prefix": "vue",
		"body": [
		  "<!DOCTYPE html>",
		  "<html lang=\"en\">",
		  "<head>",
		  "  <meta charset=\"UTF-8\">",
		  "  <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">",
		  "  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">",
		  "  <title>Document</title>",
		  "</head>",
		  "<body>",
		  "  <div id=\"app\"></div>",
		  "  <template id=\"my-app\">",
		  "",
		  "  </template>",
		  "  <script src=\"../js/vue.js\"></script>",
		  "  <script>",
		  "    const App = {",
		  "      template: '#my-app',",
		  "      data() {",
		  "        return {",
		  "",
		  "        }",
		  "      },",
		  "      methods: {",
		  "",
		  "     }",
		  "    }",
		  "",
		  "    Vue.createApp(App).mount('#app');",
		  "  </script>",
		  "</body>",
		  "</html>"
		],
		"description": ""
	  }
}

}

vue.json

{
	"Print to vue": {
		"prefix": "vue",
		"body": [
		  "  <template id=\"app\">",
		  "",
		  "  </template>",
		  "  <script src=\"../js/vue.js\"></script>",
		  "  <script>",
		  "    const App = {",
		  "      template: '#app',",
		  "      data() {",
		  "        return {",
		  "",
		  "        }",
		  "      },",
		  "      methods: {",
		  "",
		  "     }",
		  "    }",
		  "",
		  "    Vue.createApp(App).mount('#app');",
		  "  </script>",
		],
		"description": ""
	  }
}

mustache基本语法

四种方式:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  
  <div id="app"></div>

  <template id="my-app">
    <!-- 1.基本使用 -->
    <h2>{{message}} - {{message}}</h2>
    <!-- 2.表达式 -->
    <h2>{{counter * 10}}</h2>
    <h2>{{ message.split(" ").reverse().join(" ") }}</h2>
    <!-- 3.调用函数 -->
    <!-- 可以使用computed(计算属性) -->
    <h2>{{getReverseMessage()}}</h2>
    <!-- 4.三元运算符 -->
    <h2>{{ isShow ? "哈哈哈": "" }}</h2>
    <button @click="toggle">切换</button>

    <!-- 错误用法 -->
    <!-- var name = "abc" -> 赋值语句 -->
    <!-- <h2>{{var name = "abc"}}</h2>
    <h2>{{ if(isShow) {  return "哈哈哈" } }}</h2> -->
  </template>

  <script src="../js/vue.js"></script>
  <script>
    const App = {
      template: '#my-app',
      data() {
        return {
          message: "Hello World !",
          counter: 100,
          isShow: true
        }
      },
      methods: {
        getReverseMessage() {
          return this.message.split(" ").reverse().join(" ");
        },
        toggle() {
          this.isShow = !this.isShow;
        }
      }
    }

    Vue.createApp(App).mount('#app');
  </script>
</body>
</html>

基本指令

v-once指令

v-once用于指定元素或者组件只渲染一次:
当数据发生变化时,元素或者组件以及其所有的子元素将视为静态内容并且跳过;
该指令可以用于性能优化;

v-text指令

用于更新元素的 textContent

v-html指令

默认情况下,如果我们展示的内容本身是 html 的,那么vue并不会对其进行特殊的解析。
如果我们希望这个内容被Vue可以解析出来,那么可以使用 v-html 来展示;

v-pre指令

v-pre用于跳过元素和它的子元素的编译过程,显示原始的Mustache标签:
跳过不需要编译的节点,加快编译的速度;

v-cloak指令

这个指令保持在元素上直到关联组件实例结束编译。
和 CSS 规则如 [v-cloak] { display: none } 一起用时,这个指令可以隐藏未编译的 Mustache 标签直到组件实
例准备完毕。

vue3中允许template模板中有多个根元素

绑定基本属性

v-bind用于绑定一个或多个属性值,或者向另一个组件传递props值

动态绑定属性

如果属性名称不是固定的,我们可以使用 :[属性名]=“值” 的格式来定义

绑定一个对象

如果我们希望将一个对象的所有属性,绑定到元素上的所有属性,
可以直接使用 v-bind 绑定一个对象
v-bind="info" 此时会变成<div name="abc" age="18" height="1.88">123</div>

v-on绑定事件监听

缩写:@
参数:event
修饰符:

  • .stop - 调用 event.stopPropagation()。
  • .prevent - 调用 event.preventDefault()。
  • .capture - 添加事件侦听器时使用 capture 模式。
  • .self - 只当事件是从侦听器绑定的元素本身触发时才触发回调。
  • .{keyAlias} - 仅当事件是从特定键触发时才触发回调。
  • .once - 只触发一次回调。
  • .left - 只当点击鼠标左键时触发。
  • .right - 只当点击鼠标右键时触发。
  • .middle - 只当点击鼠标中键时触发。
  • .passive - { passive: true } 模式添加侦听器

条件渲染

v-showv-if的区别

  1. 用法上的区别:
  • v-show不支持template
  • v-show不可以和v-else一起使用
  1. 本质区别:
  • v-show无论是否显示到浏览器,它的dom实际都是有渲染的,只是通过css的display属性进行切换
  • v-if当条件为flase时,其对应的原生不会渲染到dom中
  1. 开发中如何选择:
  • 原生需要在显示和隐藏之间频繁切换,则使用v-show,否则就v-if

列表渲染

v-for

  • v-for也支持遍历对象,并且支持有一二三个参数:
  1. 一个参数: "value in object";
  2. 二个参数: "(value, key) in object";
  3. 三个参数: "(value, key, index) in object"

数组更新检测

Vue 将被侦听的数组的变更方法进行了包裹,所以它们也将会触发视图更新。这些被包裹过的方法包括:

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse()
    上面的方法会直接修改原来的数组,但是某些方法不会替换原来的数组,而是会生成新的数组,比如 filter()
    concat()slice()

v-for中的key是什么作用?

  • key属性主要用在Vue的虚拟DOM算法,在新旧nodes对比时辨识VNodes;
  • 如果不使用key,Vue会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法;
  • 而使用key时,它会基于key的变化重新排列元素顺序,并且会移除/销毁key不存在的元素;
举栗:向列表插入一个元素,如何最快渲染?
* 没有key的时候采用Diff算法:比较新旧节点,找到可复用的节点,比对进行增删移的操作,节省性能。 从前遍历到更改后面的节点。(三步,从头,增删,移动尾部元素)
* 有key的时候,先从前遍历到后遍历,根据元素的个数决定挂载新节点还是删除旧节点。如果是位置节点的序列,会先移动位置再比较(五步:从头,从尾,新元素挂载,旧元素删除,位置节点排序)

官方的解释对于初学者来说并不好理解,比如下面的问题:

  1. 什么是新旧nodes,什么是VNode?
  • VNode的全称是Virtual Node,也就是虚拟节点
  • 无论是组件还是元素,它们最终在Vue中表示出来的都是一个个VNode;
    template-->VNode-->真实DOM
    • 如果不止一个简单的div,而是有一大堆的元素,那么它们还会形成一个VNode Tree,也叫做虚拟DOM,然后浏览器渲染成真实DOM。
    • 为什么要有虚拟DOM而不直接渲染成真实DOM:是为了实现跨平台
  • VNode的本质是一个JavaScript的对象;
  1. 没有key的时候,如何尝试修改和复用的?
  2. 有key的时候,如何基于key重新排列的?
    key中放index的时候不会提高性能。

Vue3的Options-API

computed计算属性

  • 计算属性会基于它们的依赖关系进行缓存;
  • 在数据不发生变化时,计算属性是不需要重新计算的;
  • 但是如果依赖的数据发生变化,在使用时,计算属性依然会重新进行计算
    计算属性包含有getter和setter
    computed会判断每个key传入的是函数还是对象,然后读取option是否存在get或set函数。

watch监听器

默认情况下侦听器只会针对监听的数据本身的改变(内部发生的改变是不能侦听的)
举个栗子:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <div id="app"></div>
    <template id="my-app">
        <h2>{{info.name}}</h2>
        <button @click="changeInfo">改变info</button>
        <button @click="changeInfoName">改变infoName</button>
        <button @click="changeNBAname">改变NBAName</button>
        <!-- 改变nbaname两次及以上会报错 -->
    </template>
    <script src="../js/vue.js"></script>
    <script>
        const App = {
            template: '#my-app',
            data() {
                return {
                    info: {
                        name: "why",
                        age: 18,
                        nba:{
                            name:'snakeNBA'
                        }
                    }
                }
            },
            watch: {
                // 默认情况下侦听器只会针对监听的数据本身的改变(内部发生的改变是不能侦听的)
                /* info(newInfo, oldInfo) {
                    console.log("newValue:", newInfo, "oldValue:", oldInfo);
                    newInfo和oldInfo此时都是proxy对象
                    // 此时单纯改变info.name是侦听不到的
                }, */
                // 深度侦听
                info: {
                    handler: function (newInfo, oldInfo) {
                        console.log("newValue:", newInfo, "oldValue:", oldInfo);
                        // console.log("newValue:", newInfo.nba.name, "oldValue:", oldInfo.nba.name); //此时由于引用类型,oldinfo也指向了新对象,如果需要获取旧值需要自己深拷贝一份
                    },
                    deep: true, //深度侦听
                    // 此时info.name改变也能被侦听到
                    immediate:true
                }
            },
            methods: {
                changeInfo() {
                    this.info = {
                        name: "kobe"
                    }
                },
                changeInfoName() {
                    this.info.name = "AAA"
                },
                changeNBAname(){
                    this.info.nba.name='nbaaaaaaaaaaaaa'
                }
            }
        }

        Vue.createApp(App).mount('#app');
    </script>
</body>

</html>

注意:

  • 深度监听的新旧值指向同一个地址,官方文档有声明
  • 错误的复杂类型侦听方法:friends:[{name:'why'},{name:'code'}] watch侦听"friends[0].name":function(newName,oldName){console.log(newName,oldName)}
  • 对数组的对象进行侦听可以采用两种方法:
    1.深度侦听
    2.组件侦听

使用 $watch 的API:

  • 我们可以在created的生命周期中,使用 this.$watchs 来侦听;
    • 第一个参数是要侦听的源;
    • 第二个参数是侦听的回调函数callback;
    • 第三个参数是额外的其他选项,比如deep、immediate
      举个栗子:
created () {
                  const unwatch= this.$watch("info",function(newInfo,oldInfo) {
                    console.log(newInfo,oldInfo);
                  },{
                    deep:true,
                    immediate:true
                  })  
                }
                // unwatch()

this.$watch返回的是一个回调函数值,再次调用这个unwatch可以取消侦听

综合案例——购物车

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>

    <div id="app"></div>
    <template id="my-app">
       <template v-if="books.length>0">
        <table>
            <thead>
                <th></th>
                <th>书籍名称</th>
                <th>出版日期</th>
                <th>价格</th>
                <th>购买数量</th>
                <th>操作</th>
            </thead>
            <tbody>
                <tr v-for="(book,index) in books">
                <!-- <tr v-for="(book,index) in filterBooks"> -->
                    <td>{{index+1}}</td>
                    <td>{{book.name}}</td>
                    <td>{{book.date}}</td>
                    <td>{{formatPrice(book.price)}}</td>
                    <td>
                        <button :disabled="book.count<=1" @click="decrement(index)">-</button>
                        <span class="counter">{{book.count}}</span>
                        <button @click="increment(index)">+</button>
                    </td>
                    <td><button @click="removeOne(index)">移除</button></td>
                </tr>
            </tbody>
        </table>
        <h2>总价:{{formatPrice(totalPrice)}}</h2>
       </template>
       <template v-else>
        <h1>购物车为空~~~!</h1>
       </template>
    </template>
    <script src="../js/vue.js"></script>
    <script>
        const App = {
            template: '#my-app',
            data() {
                return {
                    books: [{
                            name: '《算法导论》',
                            date: '2006-9',
                            price: '85',
                            count: '1'
                        },
                        {
                            name: '《UNIX编程艺术》',
                            date: '2006-2',
                            price: '59',
                            count: '1'
                        },
                        {
                            name: '《编程珠玑》',
                            date: '2008-10',
                            price: '39',
                            count: '1'
                        },
                        {
                            name: '《代码大全》',
                            date: '2006-3',
                            price: '128',
                            count: '1'
                        },
                    ]
                }
            },
            computed: {
                totalPrice(){
                    let finalPrice=0;
                    for(let book of this.books){
                        finalPrice+=book.count*book.price
                    }
                    return finalPrice;
                },
                // vue3不支持过滤器了,推荐两种做法:计算属性、全局方法
                // filterBooks() {
                //     return this.books.map(item=>{
                //         item.price='¥'+item.price;
                //         return item;
                //     })
                // }
            },
            methods: {
                increment(index) {
                    this.books[index].count++;
                },
                decrement(index) {
                    this.books[index].count--;
                },
                removeOne(index) {
                    this.books.splice(index, 1)
                },
                formatPrice(price){
                    return '¥'+price
                }
            }
        }

        Vue.createApp(App).mount('#app');
    </script>
</body>
<style>
    table {
        border: 1px solid #e9e9e9;
        border-collapse: collapse;
        border-spacing: 0;
    }

    th,
    td {
        padding: 8px, 16px;
        border: 1px solid #e9e9e9;
        text-align: left;
    }

    th {
        background-color: #f7f7f7;
        color: #5c6b77;
        font-weight: 600;
    }

    .counter {
        margin: 0, 5px;
    }
</style>

</html>
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值