Vue3学习

目录

一、Vue3基础语法

1.1. Vue3新特性

更好的性能、更小的包体积、更好的TypeScript集成、更优秀的API设计

1.2. Vue3带来的变化(性能)

  • 使用Proxy进行数据劫持
    Vue2是使用Object.defineProperty来劫持数据的getter和setter方法的;这种方式存在一个缺陷就是当给对象添加或者删除属性时,是无法劫持和监听的;
    Vue3是使用Proxy进行数据劫持。
  • 删除了一些不必要的API:
    移除了实例上的$on, $off 和 $once;
    移除了一些特性:如filter、内联模板等;
  • 包括编译方面的优化:
    生成Block Tree、Slot编译优化、diff算法优化;

1.3 CDN

CDN称之为内容分发网络(Content Delivery Network或Content Distribution Network),它是指通过相互连接的网络系统,利用最靠近每个用户的服务器;更快、更可靠地将音乐、图片、视频、应用程序及其他文件发送给用户;来提供高性能、可扩展性及低成本的网络内容传递给用户。
开源的CDN服务器:国际上使用比较多的是unpkg、 JSDelivr、cdnjs。

1.4. Vue初体验

<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
  //第一种写法
  /* const why = {
    template: "<h2>嗨</h2>"
  }
  const app = Vue.createApp(why);
  app.mount("#app") */

  //第二种简写 
  Vue.createApp({
    template: `<h2>嗨嗨嗨</h2>`
  }).mount("#app") 
</script>

1.5. 声明式和命令式

  • 两种不同的编程范式:
    命令式编程声明式编程
    命令式编程关注的是 “how to do”;声明式编程关注的是 “what to do”,由框架(机器)完成 “how”的过程;
  • 原生的实现过程中
    我们每完成一个操作,都需要通过JavaScript编写一条代码,来给浏览器一个指令,这样的编写代码的过程,我们称之为命令式编程
    在早期的原生JavaScript和jQuery开发的过程中,我们都是通过这种命令式的方式在编写代码的。
  • Vue的实现过程中
    我们会在createApp传入的对象中声明需要的内容,模板template、数据data、方法methods,这样的编写代码的过程,我们称之为是声明式编程
    目前Vue、React、Angular的编程模式,我们称之为声明式编程。

1.6. MVVM模型

  • MVC和MVVM都是一种软件的体系结构
    MVC是Model – View –Controller的简称,是在前期被使用非常框架的架构模式,比如iOS、前端;
    MVVM是Model-View-ViewModel的简称,是目前非常流行的架构模式。
  • 通常情况下,我们也经常称Vue是一个MVVM的框架
    Vue官方其实有说明,Vue虽然并没有完全遵守MVVM的模型,但是整个设计是受到它的启发的。

1.7. template属性

template属性表示的是Vue需要帮助我们渲染的模板信息
方式一:使用script标签,并且标记它的类型为 x-template;

<script type="x-template" id="why">
  <div>
    <h2>{{counter}}</h2>
    <button @click='increment'>+1</button>
    <button @click='decrement'>-1</button>
  </div>
</script>

方式二:通常使用template标签,因为不会被浏览器渲染,并设置id;

<template id="why">
 <div>
   <h2>{{counter}}</h2>
   <button @click='increment'>+1</button>
   <button @click='decrement'>-1</button>
 </div>
</template>

在createApp的对象中,我们需要传入的template以 # 开头:如果字符串是以 # 开始,那么它将被用作 querySelector,并且使用匹配元素的 innerHTML 作为模板字符串;

1.8. data属性

Vue3的data属性必须传入一个函数(否则就会直接在浏览器中报错),并且该函数需要返回一个对象。
data中返回的对象会被Vue的响应式系统劫持,之后对该对象的修改或者访问都会在劫持中被处理,所以我们在template中通过 {{counter}} 访问counter,可以从对象中获取到数据;所以我们修改counter的值时,template中的 {{counter}}也会发生改变。

1.9. methods属性

methods属性是一个对象,通常我们会在这个对象中定义很多的方法:这些方法可以被绑定到 template 模板中;在该方法中,我们可以使用this关键字来直接访问到data中返回的对象的属性。
注意:不能使用箭头函数来定义methods函数(例如plus: => this.a++)。理由是箭头函数绑定了父级作用域的上下文,所以this将不会按照期望指向组件实例,this.a将是undefined。
前端面试之彻底搞懂this指向

1.10. vue3的源码

下载源代码,第一步安装Vue源码项目相关的依赖,执行yarn install;第二步对项目执行打包操作,执行yarn dev

1.11. VSCode设置代码片段

左上角Code——首选项——用户片段——选择html
具体的步骤如下:
第一步:复制自己需要生成代码片段的代码;
第二步:https://snippet-generator.app/在该网站中生成代码片段;
第三步:在VSCode中配置代码片段;

1.12. Mustache双大括号语法

data返回的对象是有添加到Vue的响应式系统中,当data中的数据发生改变时,对应的内容也会发生更新,当然,Mustache中不仅仅可以是data中的属性,也可以是一个JavaScript的表达式

<!-- 基本使用 -->
<h2>{{message}}</h2>

<!-- js表达式 -->
<h2>{{counter * 2}}</h2>

<!-- 调用methods中的函数 -->
<h2>{{getReverseMessage()}}</h2>

<!-- 三元运算符 -->
<h2>{{isShow ? "哈哈哈" : "" }}</h2>
<button @click="toggle">切换</button>

1.13. 基本指令

  • v-once
    用于指定元素或者组件只渲染一次:当数据发生变化时,元素或者组件以及其所有的子元素<.font>将视为静态内容并且跳过;该指令可以用于性能优化
  • v-text
    用于更新元素的 textContent<h2 v-text="message"></h2>等价于<h2>{{message}}</h2>
    v-text缺点是不够灵活。
  • v-html
    解析data中的html内容<h2 v-html="message"></h2>message: "<div style='color:red'>哈哈哈</div> "
  • v-pre
    用于跳过元素和它的子元素的编译过程,显示原始的Mustache标签
  • v-cloak
    用于保持在元素上直到关联组件实例结束编译。和 CSS 规则如 [v-cloak] { display: none } 一起用时,这个指令可以隐藏未编译的 Mustache 标签直到组件实例准备完毕。

1.14. v-bind绑定属性

v-bind用于绑定一个或多个属性值,或者向另一个组件传递props值
v-bind的语法糖是:
可动态绑定的属性:如图片的链接src、网站的链接href、动态绑定一些类、样式等

①绑定属性img、href

<a :href="link">百度一下</a>
<img :src="imgSrc" alt="">

②绑定class有两种方式:对象语法;数组语法;

<!-- -----对象语法----- -->
<!-- {'active': boolean} 引号不写也可以 -->
<div :class="{'active': isActive}">哈哈哈</div>
<button @click="toggle">切换</button>

<!-- 可以有多个键值对(多个动态class) -->
<div :class="{'active': isActive, 'titles': true}">哈哈哈</div>

<!-- 默认的class和动态的class结合 -->
<div class="haha hehe" :class="{'active': isActive, 'titles': true}">哈哈哈</div>

<!-- 绑定对象 -->
<div class="haha hehe" :class="classObj">哈哈哈</div>

<!-- 从methods(computed)中获取 -->
<div class="haha hehe" :class="getClassObj()">哈哈哈</div>
<script>
data() {
  return {
    isActive: true,
    classObj: {
      active: true,
      titles: true,
      ceshi: true
    }
  }
},
methods: {
  toggle() {
    this.isActive = !this.isActive
  },
  getClassObj() {
    return {
      active: true,
      titles: true,
      ceshi: true,
      lala: true
    }
  }
}
</script>
<!-- -----数组语法----- -->
<!-- 直接传入一个数组 -->
<div :class="['active', titles]">哈哈哈</div>

<!-- 数组中使用三元运算符 -->
<div :class="['active', titles, isActive? 'active2': '']">哈哈哈</div>

<!-- 数组中使用对象语法 -->
<div :class="['active', titles, {active3 :isActive}]">哈哈哈</div>
<script>
data() {
  return {
    titles: 'cba',
    isActive: true
  }
}
</script>

③绑定style有两种方式:对象语法;数组语法;
某些样式我们需要根据数据动态来决定,比如某段文字的颜色,大小等等;
CSS property 名可以用驼峰式 (camelCase) 短横线分隔 (kebab-case,记得用引号括起来)来命名;
注意:style中的class名用-拼接的属性key需要加引号;value值必须加引号。

<!-- -----对象语法----- -->
<!-- 基本使用:传入一个对象,对象内容基本都是确定的 -->
<div :style="{color: textColor, fontSize: '30px', 'background-color': 'blue'}">哈哈哈</div>

<!-- 基本使用:传入一个对象,值来自于data -->
<div :style="{color: 'red', fontSize: size+'px', 'background-color': 'blue'}">哈哈哈</div>

<!-- 对象数据:直接在data中定义好对象在这里使用 -->
<div :style="styleObj">哈哈哈</div>

<!-- 调用一个方法 -->
<div :style="getStyleObj()">呵呵呵</div>

<script>
data() {
  return {
    size: '18px',
    textColor: 'green',
    styleObj: {
      fontSize: '50px',
      fontWeight: 700,
      backgroundColor: 'red'
    }
  }
},
methods: {
  getStyleObj() {
    return {
      fontSize: '60px',
      fontWeight: 700,
      backgroundColor: 'red'
    }
  }
}
</script>

:style的数组语法可以将多个样式对象应用到同一个元素上:

<!-- -----数组语法----- -->
<div :style="[style1Obj, style2Obj]">哈哈哈</div>
<script>
data() {
  return {
    style1Obj: {
      color: 'red',
      fontSize: '30px'
    },
    style2Obj: {
      textDecoration: "underline"
    }
  }
}
</script>

④动态绑定属性:如果属性名称不是固定的,我们可以使用:[属性名]=“值”的格式来定义,这种绑定的方式,我们称之为动态绑定属性

<h2 :[name]="value">{{message}}</h2>
<script>
data() {
 return {
   message: '加油',
   name: 'why',
   value: 'kobe'
 }
}
</script>
<!-- 最终渲染成 <h2 why="kobe">加油</h2> -->

⑤绑定一个对象:将一个对象的所有属性,绑定到元素上显示所有属性
案例:info对象会被拆解成div的各个属性

<h2 v-bind="info">{{message}}</h2>
<script>
data() {
  return {
    message: '呵呵',
    info: {
      name: 'why',
      age: 18,
      height: 188
    }
  }
}
</script>
<!-- 最终渲染成 <h2 name="why" age="18" height="188">呵呵</h2> -->

1.15. v-on绑定事件

我们需要经常和用户进行各种各样的交互,监听用户发生的事件:比如点击、拖拽、键盘事件等等;
v-on指令,语法糖(缩写)是@,v-on:click可以写成@click

①基本使用

<!-- ---1.基本使用--- -->
<!-- 绑定一个表达式 -->
<h2 v-on:click="counter++">{{counter}}</h2>

<!-- 绑定到一个methods方法中 -->
<h2 @click="btnClick">{{counter}}</h2>

<!-- 绑定鼠标移动事件 -->
<h2 @mousemove="mouseMove">{{counter}}</h2>

<!-- ---2.绑定对象(绑定多个事件)--- -->
<h2 v-on="{click: btnClick, mousemove:mouseMove}">特殊按钮</h2>

<script>
data() {
  return {
    counter: 100
  }
},
methods: {
  btnClick() {
    console.log("btnClick")
  },
  mouseMove() {
    console.log("mouseMove")
  }
}
</script>

②参数传递
情况一:如果该方法不需要额外参数,那么方法后的()可以不添加。但是注意:如果方法本身中有一个参数,那么会默认将原生事件event参数传递进去;
情况二:如果需要同时传入某个参数,同时需要event时,可以通过$event传入事件。

<!-- 当按钮被点击时,浏览器会生成一个event对象 -->
<!-- 默认传入event对象, 可以在方法中获取 -->
<button @click="btn1Click">按钮1</button>

<!-- $event可以获取到事件发生时的事件对象 -->
<button @click="btn2Click($event, 'coderwhy', 18, 188)">按钮2</button>
<script>
methods: {
  btn1Click(event) {
    console.log(event);
  },
  btn2Click(event, name, age, height) {
    console.log(event, name, age, height);
  }
}
</script>

③v-on的修饰符
修饰符相当于对事件进行了一些特殊的处理:
.stop - 调用 event.stopPropagation();停止冒泡。
.prevent - 调用 event.preventDefault();阻止默认事件。
.capture - 添加事件侦听器时使用 capture 模式。
.self - 只当事件是从侦听器绑定的元素本身触发时才触发回调。
.{keyAlias} - 仅当事件是从特定键触发时才触发回调。
.once - 只触发一次回调。
.left - 只当点击鼠标左键时触发。
.right - 只当点击鼠标右键时触发。
.middle - 只当点击鼠标中键时触发。
.passive - { passive: true } 模式添加侦听器。

<!-- 点击按钮两个都有事件,加上.stop阻止冒泡,就只有btnClick事件,不会把事件向外冒泡-->
<div @click="divClick">
  <button @click.stop="btnClick">按钮</button>
</div>
<!-- 监听键的点击。默认情况下,键盘抬起的时候输入每一个字都会执行,加上.enter输完所有的敲回车才会执行 -->
<input type="text" @keyup.enter="enterKeyup">
    
<script>
methods: {
	divClick() {
	  console.log("divClick")
	},
	btnClick() {
	  console.log("btnClick")
	},
	enterKeyup() {
    console.log("enterKeyup", event.target.value)//拿到输入的内容
  }
}
</script>

1.16. 条件渲染(v-if)

在某些情况下,我们需要根据当前的条件决定某些元素或组件是否渲染,这个时候我们就需要进行条件判断了。这些内容只有在条件为true时,才会被渲染出来。
Vue提供了下面的指令来进行条件判断: v-if、v-else、v-else-if、v-show

<input type="text" v-model="score">
<h2 v-if="score > 90">优秀</h2>
<h2 v-else-if="score > 60">良好</h2>
<h2 v-else>不及格</h2>

<script>
data() {
  return {
    score: 92
  }
}
</script>

1.17. template元素

因为v-if是一个指令,所以必须将其添加到一个元素上,但是我们并不希望div这种元素被渲染,这个时候,我们可以选择使用template;
template元素可以当做不可见的包裹元素,并且在v-if上使用,但是最终template不会被渲染出来;有点类似于小程序中的block。

1.18. v-show和v-if的区别

用法区别:v-show不支持template;v-show不可以和v-else一起使用。
本质区别:v-show元素无论是否需要显示到浏览器上,它的DOM实际都是有渲染的,只是通过CSS的display属性来进行切换;v-if当条件为false时,其对应的原生不会被渲染到DOM中。
如何进行选择呢?当需要在显示与隐藏之间切片很频繁时,使用v-show;如果不会频繁的切换,使用v-if。

1.19. 列表渲染(v-for)

①v-for基本使用(遍历数组)
v-for的基本格式是“item in 数组”或者“item of 数组”
 数组通常是来自data或者prop,也可以是其他方式;
 item是我们给每项元素起的一个别名,这个别名可以自己来定义;
在遍历一个数组的时候会经常需要拿到数组的索引
 如果我们需要索引,可以使用格式: “(item, index) in 数组”
 注意上面的顺序:数组元素项item是在前面的,索引项index是在后面的;

<!--没有索引值-->
<li v-for ="item in movies">{{item}}</li> 
<!--获取索引值-->
<li v-for ="(item, index) in movies">{{index+1}}---{{item}}</li>

②v-for遍历对象
v-for也支持遍历对象,并且支持有一二三个参数:
 一个参数: “value in object”;
 二个参数: “(value, key) in object”;
 三个参数: “(value, key, index) in object”;

<li v-for="(value, key, index) in info">{{value}}-{{key}}-{{index}}</li>
<script>
data() {
  return {
    movies: [
      "星际穿越",
      "盗梦空间",
      "大话西游",
      "教父",
      "少年派"
    ],
    info: {
      name: "why",
      age: 18,
      height: 1.88
    }
  }
}
</script>

③v-for遍历数字:每一个item都是一个数字

<li v-for="(num, index) in 10">{{num}}-{{index}}</li>

v-for中的key是什么作用
官方解释:key的作用主要是为了高效的更新虚拟DOM。key属性主要用在Vue的虚拟DOM算法,在新旧nodes对比时辨识VNodes;如果不使用key,Vue会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法;而使用key时,它会基于key的变化重新排列元素顺序,并且会移除/销毁key不存在的元素。

1.20. 虚拟节点(VNode)

VNode的全称是Virtual Node
HTML元素创建出来的VNode(虚拟节点);
无论是组件还是元素,它们最终在Vue中表示出来的都是一个个VNode;
VNode的本质是一个JavaScript的对象;
在这里插入图片描述
在这里插入图片描述

1.21. 虚拟DOM(Virtual Dom)

如果我们不是只有一个简单的div,而是有一大堆的元素,那么它们应该会形成一个VNode Tree(多个VNode形成的树结构)
在这里插入图片描述为什么不直接渲染成真实DOM?为了做多平台的适配(跨平台)

1.22. 数组更新检测

直接修改原来的数组的方法(以下是响应式的):
在这里插入图片描述以下三个方法不会替换原来的数组,会生成新的数组的方法(以下不是响应式的):filter()、 concat() 和 slice()

1.23. 计算属性computed

复杂data的处理方式,在某些情况,可能需要对数据进行一些转化后再显示,或者需要将多个数据结合起来进行显示,有以下三种方式:
①在模板中使用表达式,可以非常方便的实现,但是设计它们的初衷是用于简单的运算;在模板中放入太多的逻辑会让模板过重和难以维护,并且如果多个地方都使用到,那么会有大量重复的代码;多次使用的时候,很多运算也需要多次执行,没有缓存;
②将逻辑抽取到一个method中,这种做法有一个弊端,就是所有的data使用过程都会变成了一个方法的调用;在多次使用方法的时候,没有缓存,也需要多次计算;
③使用计算属性computed。计算属性看起来像是一个函数,但是我们在使用的时候不需要加(),并且计算属性是有缓存的。

对于任何包含响应式数据的复杂逻辑,都应该使用计算属性;计算属性将被混入到组件实例中。所有getter和setter的this上下文自动地绑定为组件实例。

  • 计算属性的缓存
    计算属性是有缓存的,当多次使用计算属性时,计算属性中的运算只会执行一次,而methods当多次使用的时候,没有缓存,需要多次计算;
    因为计算属性会基于它们的依赖关系进行缓存,在数据不发生变化时,计算属性是不需要重新计算的,如果依赖的数据发生变化,在使用时,计算属性依然会重新进行计算

  • 计算属性的setter和getter
    计算属性在大多数情况下,只需要一个getter方法即可,所以我们会将计算属性直接写成一个函数

<div id="app"></div>
<template id="my-app">
  <h2>{{fullName}}</h2>
</template>
<script>
 Vue.createApp({
   template: '#my-app',
   data() {
     return {
       firstName: 'xixi',
       lastName: 'hehe'
     }
   },
   computed: {
     //原始写法:计算属性一般是没有set方法,只读属性
     /* fullName: {
       //set: function () {},
       get: function () {
         return this.firstName + ' ' + this.lastName
       }
     } */
     //最终简便写法:省略get方法 ES5写法
     /* fullName: function () {
       return this.firstName + ' ' + this.lastName
     } */
     //最终简便写法:省略get方法 ES6增强写法
     fullName() {
       return this.firstName + ' ' + this.lastName
     }
   }
 }).mount("#app")
</script>

1.24. 侦听器watch

  • 什么是侦听器?
    开发中我们在data返回的对象中定义了数据,这个数据通过插值语法等方式绑定到template中;当数据变化时,template会自动进行更新来显示最新的数据;但是在某些情况下,我们希望在代码逻辑中监听某个数据的变化,这个时候就需要用侦听器watch来完成了。(一般用在想侦听data数据发生变化时需要进行一些逻辑处理的时候使用)

  • 使用场景
    比如现在我们希望用户在input中输入一个问题;每当用户输入了最新的内容,我们就获取到最新的内容,并且使用该问题去服务器查询答案;那么,我们就需要实时的去获取最新的数据变化。
    在这里插入图片描述

  • 侦听器watch的配置选项
    info: {name: 'aa', age: 18},只能侦听info引用的变化(把原Info改成新的指针对象this.info={name: 'bb'}),对于内部属性的变化是不会做出响应的(this.info.name='cc'内部属性发生的改变是不能侦听的)。
    实现侦听内部属性的方法有以下3个:

①配置选项 深度侦听

watch: {
  //深度侦听/立即执行
	info: {
	  handler: function (newInfo, oldInfo) {
	    console.log("新值:", newInfo, "旧值:", oldInfo)
	  },
	  deep: true,//深度侦听
	  immediate: true //立即执行
	}
}

②Vue2文档中有提到的是侦听对象的属性

watch: {
 "info.name": function (newName, oldName) {
   console.log("新name:", newName, "旧name:", oldName)
 }
}

③$watch实例方法

created() {
  this.$watch('info', (newInfo, oldInfo) => {
    console.log("新值:", newInfo, "旧值:", oldInfo)
  }, { deep: true, immediate: true })
}

1.25. v-model的基本使用

表单提交是开发中非常常见的功能,也是和用户交互的重要手段:
比如用户在登录、注册时需要提交账号密码;
比如用户在检索、创建、更新信息时,需要提交一些数据;
v-model指令可以在表单 input、textarea以及select元素上创建双向数据绑定;它会根据控件类型自动选取正确的方法来更新元素。

  • v-model的原理
    v-bind绑定value属性的值;
    v-on绑定input事件监听到函数中,函数会获取最新的值赋值到绑定的属性中;
<input v-model="searchText">
<!--等价于-->
<input :value="searchText" @input="searchText=$event.target.value">
  • v-model绑定textarea、checkbox、radio、select
    checkbox:单个勾选框v-model即为布尔值;多个复选框,因为可以选中多个,所以对应的data中属性是一个数组。还需要传一个value值,不写value无法区分选中了哪个。
    select:也可以多选。
<div id="app"></div>
<template id="my-app">
  <!-- 1.绑定textarea -->
  <label for="intro">
    自我介绍
    <textarea name="intro" id="intro" cols="30" rows="10" v-model="intro"></textarea>
  </label>
  <h2>intro: {{intro}}</h2>

  <!-- 2.checkbox -->
  <!-- 2.1.单选框 -->
  <label for="agree">
    <input id="agree" type="checkbox" v-model="isAgree"> 同意协议
  </label>
  <h2>isAgree: {{isAgree}}</h2>

  <!-- 2.2.多选框 -->
  <span>你的爱好: </span>
  <label for="basketball">
    <input id="basketball" type="checkbox" v-model="hobbies" value="basketball"> 篮球
  </label>
  <label for="football">
    <input id="football" type="checkbox" v-model="hobbies" value="football"> 足球
  </label>
  <label for="tennis">
    <input id="tennis" type="checkbox" v-model="hobbies" value="tennis"> 网球
  </label>
  <h2>hobbies: {{hobbies}}</h2>

  <!-- 3.radio -->
  <span>你的性别: </span>
  <label for="male">
    <input id="male" type="radio" v-model="gender" value="male"></label>
  <label for="female">
    <input id="female" type="radio" v-model="gender" value="female"></label>
  <h2>gender: {{gender}}</h2>

  <!-- 4.select  multiple是多选的意思-->
  <span>喜欢的水果: </span>
  <select v-model="fruit" multiple size="2">
    <option value="apple">苹果</option>
    <option value="orange">橘子</option>
    <option value="banana">香蕉</option>
  </select>
  <h2>fruit: {{fruit}}</h2>
</template>

<script src="../js/vue.js"></script>
<script>
  const App = {
    template: '#my-app',
    data() {
      return {
        intro: "Hello World",
        isAgree: false,
        hobbies: ["basketball"],
        gender: "",
        fruit: "orange"
      }
    }
  }
  Vue.createApp(App).mount('#app');
</script>
  • v-model的值绑定
    在真实开发中,我们的数据可能是来自服务器的,那么我们就可以先将值请求下来,绑定到data返回的对象中, 再通过v-bind来进行值的绑定,这个过程就是值绑定
  • 修饰符
    ①lazy:v-model.lazy="message"默认情况下,v-model在进行双向绑定时,绑定的是input事件,那么会在每次内容输入后就将最新的值和绑定的属性进行同步;如果我们在v-model后跟上lazy修饰符,那么会将绑定的事件切换为change事件,只有在提交时(比如回车)才会触发;
    ②number:v-model.number="score"v-model绑定后的值是string类型,换为数字类型可以使用.number修饰符(在进行逻辑判断时,如果是一个string类型,会进行隐式转换)
    ③trim:v-model.trim="msg"如果要自动过滤用户输入的首尾空白字符,可以给v-model添加 trim修饰符

1.26. 实例方法

实例方法

二、前端模块化

2.1. CommonJS导入导出

①通过CommonJS导出

var flag = true
module.exports = {
 flag
}

②通过CommonJS导入

let { flag } = require('./main.js')

2.2. ES6导入导出

2.2.1. export的基本使用(导出变量)

①方式一

let name = 'tu'
let age = 18
let height: 160
export {
 name, age ,height
}

②方式二

export let name = 'tu'
export let age = 18
export let height = 160

2.2.2. ES6的export导出函数或类

①导出函数

//方式一
export function test(){

}
//方式二
function test(){
}
export { test }

②导出类

//方式一
export class Preson(){
}
//方式二
class Preson(){
}
export { Preson }

③export default
导入者可以自己来命名

export default address //default在同一个模块中,不允许同时存在多个
export default function () {
}
import addr from './info.js'

2.2.3. ES6的import导入

①需要在HTML代码中引入js文件,并且类型需要设置为module

<script src="info.js" type="module"></script>
<script src="main.js" type="module"></script>

②import指令用于导入模块中的内容,比如main.js的代码

import { name, age, height } from './info.js'

③模块中所有的信息都导入

import * as info from './info.js'

④只有在export default时才可以以这种方式(相当于自己起名字)导入

import HelloVuex from './components/HelloVuex'

三、Webpack

记录:
①新下载的项目要npm install安装package.json里的所有依赖;
②main.js为项目的入口文件;
③第一次打包时,打包后的html文件中的路径默认是/开头,需要在vue.config.js中设置publicPath: './'(CLI提供的属性)(vue-cli3.x已经没有了webpack.config.js文件。取而代之的是创建一个vue.config.js文件)
④webpack是在node环境运行的,要通过module.exports导出;
–save-dev是开发时依赖,可以简写为-D,项目打包后不需要继续使用的;
⑥跨域问题,部署的时候和后端配合,使用nginx解决跨域,还可以使用proxy代理;
⑦如果你的工程模块依赖非常简单,甚至是没有用到模块化的概念,只需要进行简单的合并、压缩,就使用grunt/gulp即可。grunt/gulp更加强调的是前端流程的自动化,模块化不是它的核心。webpack更加强调模块化开发管理,而文件压缩合并、预处理等功能,是他附带的功能。

3.1. 认识webpack

  • webpack is a static module bundler for modern JavaScript applications(webpack是一个静态的模块化打包工具,为现代的JavaScript应用程序)
    打包bundler:webpack可以将帮助我们进行打包,将webpack中的各种资源模块进行打包合并成一个或多个包(Bundle)。(并且在打包的过程中,还可以对资源进行处理,比如压缩图片,将scss转成css,将ES6语法转成ES5语法,将TypeScript转成JavaScript等等操作。)
    静态的static:可以将代码打包成最终的静态资源(部署到静态服务器);
    模块化module:webpack默认支持各种模块化开发,ES Module、CommonJS、AMD等;
    现代的modern:正是因为现代前端开发面临各种各样的问题,才催生了webpack的出现和发展。(并且会帮助我们处理模块间的依赖关系。)
  • 脚手架都是依赖于webpack的
  • webpack可以做什么
    ①进行模块化开发,并且会帮助我们处理模块间的依赖关系;
    ②使用一些高级的特性来加快我们的开发效率或者安全性,比如通过ES6+、TypeScript开发脚本逻辑,通过sass、less等方式来编写css样式代码;
    ③开发过程中,实时的监听文件的变化来并且反映到浏览器上,提高开发的效率;
    ④比如开发完成后我们还需要将代码进行压缩、合并以及其他相关的优化

3.2. Webpack的使用前提和安装

Webpack的运行是依赖Node环境的,需要先安装Node.js,并且同时会安装npm。查看node版本node -v

  • webpack和webpack-cli的关系
    执行webpack命令,会执行node_modules下的.bin目录下的webpack;
    webpack在执行时是依赖webpack-cli的,如果没有安装就会报错;
    而webpack-cli中代码执行时,才是真正利用webpack进行编译和打包的过程;
  • 安装命令
    全局安装npm install webpack webpack-cli –g
    局部安装npm install webpack webpack-cli –D

2.3. Webpack的默认打包(全局)

  • 在目录下直接执行webpack命令进行打包,之后运行打包之后的代码;
  • 生成一个dist文件夹,里面存放一个main.js的文件,就是我们打包之后的文件:
    这个文件中的代码被压缩和丑化了;另外我们发现代码中依然存在ES6的语法,比如箭头函数、const等,这是因为默认情况下webpack并不清楚我们打包后的文件是否需要转成ES5之前的语法,后续我们需要通过babel来进行转换和设置。
  • 通过配置来指定入口和出口
    当我们运行webpack时,webpack会查找当前目录下的src/index.js作为入口,所以,如果当前项目中没有存在src/index.js文件,那么会报错;我们也可以通过配置来指定入口和出口npx webpack --entry ./src/main.js --output-path ./build

2.4. 创建局部的webpack

任何时候开发项目都有自己局部的webpack

  • 第一步:创建package.json文件,用于管理项目的信息、库依赖等;
    npm init
  • 第二步:安装局部的webpack;
    npm install webpack webpack-cli –D
  • 第三步:使用局部的webpack;
    npx webpack
  • 第四步:在package.json中创建scripts脚本,执行脚本打包即可;
    配置完最后再执行npm run build打包即可
"script": {
  "build": "webpack"
}

2.5. 配置文件(配置入口和出口)

在根目录下创建一个webpack.config.js文件,来作为webpack的配置文件:

//配置入口和出口
const path = require('path') //依赖包

module.exports = {
	//入口:可以是字符串/数组/对象
	entry: './scr/main.js',
	//出口:通常是一个对象,里面至少包含path和filename两个重要的属性
	output: {
		filename: 'bundle.js', 
		path: path.resolve(__dirname, './dist') //需要动态获取路径;__dirname获取当前文件所在路径
	}
}

2.6. 指定配置文件

比如我们将webpack.config.js修改成了wk.config.js,这个时候我们可以通过 --config 来指定对应的配置文件,可以在package.json中增加一个新的脚本:

"script": {
  "build": "webpack --config wk.config.js"
}

之后执行npm run build来打包即可

2.7. 打包css和资源

css-loader、style-loader、less-loader、postcss-loader、file-loader、url-loader、babel-loader、vue-loader

  • 什么是loader?
    loader可以用于对模块的源代码进行转换;
    css-loader:将css文件也看成是一个模块,通过import来加载这个模块;在加载这个模块时,webpack其实并不知道如何对其进行加载,需要一个可以读取css文件的loader来完成这个功能;
    style-loader:css-loader只是负责将.css文件进行解析,并不会将解析之后的css插入到页面中,如果我们希望再完成插入style的操作,还需要style-loader;
    less-loader:如何让我们的环境支持这些预处理器?less、sass等编写的css需要通过less工具转换成普通的css;安装lessnpm install less -D,执行npx lessc ./src/css/title.less title.css编译转换;再使用less-loader自动转换,来自动使用less工具转换less到css;
    postcss-loader:借助于构建工具来对css进行处理npm install postcss-loader -D;注意:因为postcss需要有对应的插件才会起效果,所以我们需要配置它的plugin;也可以将这些配置信息放到一个单独的文件中进行管理,在根目录下创建postcss.config.js;
    file-loader:作用就是帮助我们处理import/require()方式引入的一个文件资源,并且会将它放到我们输出的文件夹中;
    url-loader
    babel-loader
    vue-loader

  • loader配置方式
    安装css-loader:npm install css-loader -D
    安装style-loader:npm install style-loader -D
    安装less-loader: npm install less-loader -D
    安装file-loader:npm install file-loader -D

  • PostCSS工具
    PostCSS是一个通过JavaScript来转换样式的工具,这个工具可以帮助我们进行一些CSS的转换和适配,比如自动添加浏览器前缀、css样式的重置,实现这些功能,我们需要借助于PostCSS对应的插件,安装:npm install postcss postcss-cli -D
    插件①autoprefixer:添加前缀的插件,安装npm install autoprefixer -D,直接使用使用postcss工具,并且制定使用autoprefixernpx postcss --use autoprefixer -o end.css ./src/css/style.css
    插件②postcss-preset-env:事实上,在配置postcss-loader时,我们配置插件并不需要使用autoprefixer,postcss-preset-env也是一个postcss的插件,它可以帮助我们将一些现代的CSS特性,转成大多数浏览器认识的CSS,并且会根据目标浏览器或者运行时环境 添加所需的polyfill,也包括会自动帮助我们添加autoprefixer,安装npm install postcss-preset-env -D,之后,直接修改掉之前的autoprefixer即可;

/*postcss.config.js*/
module.exports = {
  plugins: [
  	//在使用某些postcss插件时,也可以直接传入字符串
    require("postcss-preset-env")
  ]
}
/*webpack.config.js*/
const path = require('path');

module.exports = {
  entry: "./src/main.js",
  output: {
    path: path.resolve(__dirname, "./build"),
    filename: "bundle.js"
  },
  module: {
    rules: [
      {
        test: /\.css$/, //正则表达式
        // 1.loader的写法(语法糖)
        // loader: "css-loader"

        // 2.完整的写法
        use: [
          // {loader: "css-loader"}
          //因为loader的执行顺序是从后到前(或者说从下到上),所以我们需要将style-loader写到css-loader的前面
          "style-loader",
          "css-loader",
          "postcss-loader"
          /*{
             loader: "postcss-loader",
             options: {
               postcssOptions: {
                 plugins: [
                   require("autoprefixer")
                 ]
               }
            }
          }*/
        ]
      },
      {
        test: /\.less$/,
        //test: /\.(less|css)$/,
        use: [
          "style-loader",
          "css-loader",
          "less-loader"
        ]
      }
    ]
  }
}

2.8. Plugin

1、CleanWebpackPlugin
安装npm install clean-webpack-plugin -D
2、HtmlWebpackPlugin
安装npm install html-webpack-plugin -D
3、DefinePlugin

4、CopyWebpackPlugin
安装npm install copy-webpack-plugin -D

2.9. 模块热替换(HMR)

2.10.代码分包

三、Vue3组件化开发

记录:
①scope会添加data-v-fxxx的属性,子组件最好加上根元素div;
②导入组件可以不跟后缀名,因为当前创建的项目是基于vuecli,vuecli是基于webpack,webpack里面有一个extensions会配置一些后缀名。(尽量加上后缀名,使用组件时会有提示,还可以ctrl点进去跟踪源组件)
③响应式的API:computed函数,computed是vue3的新特性;
$refs的使用:在某个子组件上使用ref属性设置标识,然后通过this.$refs.scroll.message访问到该组件。

3.1. 认识组件化开发

3.2. 注册全局组件

3.3. 注册局部组件

3.4. 组件的通信

3.4.1. 父子组件的通信方式props/$emit

1、父传子:通过props
Props是你可以在组件上注册一些自定义的attribute(属性);父组件给这些attribute赋值,子组件通过attribute的名称获取到对应的值。

  • props的值有两种方式:
    ①字符串数组。数组中的字符串就是attribute的名称(只能传入的attribute的名称,并不能对其进行任何形式的限制);
    ②对象类型。对象类型我们可以在指定attribute名称的同时,指定它需要传递的类型、是否是必须的、 默认值等等;type的类型都可以是String、Number、Boolean、Array、Object、Date、Function、Symbol;
<!--父组件-->
<template>
	<!--两种方式:title是直接传递数据,banner是动态绑定数据-->
	<HelloWorld title="is title msg" :banner="bannerMsg" />
</template>

<script>
import HelloWorld from "./components/HelloWorld.vue";

export default {
  name: "App",
  components: {
    HelloWorld,
  },
  data() {
    return {
      bannerMsg: "我是banner",
    };
  },
};
</script>

<style></style>

<!--子组件-->
<!-----Props的数组用法----->
<template>
  <div>
    <h1>{{ title }}</h1>
    <h1>{{ banner }}</h1>
  </div>
</template>

<script>
export default {
  name: "HelloWorld",
  props: ["title", "banner"],
};
</script>

<style scoped></style>

<!-----Props的对象用法----->
<template>
  <div>
    <h1>{{ title }}</h1>
    <h1>{{ banner }}</h1>
  </div>
</template>

<script>
export default {
  name: "HelloWorld",
  props: {
    //基础的类型检查
    title: String,
    //带有默认值的对象
    banner: {
      type: String,
      required: true,
      defalut() {
        return {};
      },
    },
  },
};
</script>

<style scoped></style>
  • 注意:
    ①attribute名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符,camelCase (驼峰命名法) 的prop名需要使用其等价的 kebab-case (短横线分隔命名) 命名
    ②子组件中的模板中必须要在最外层写上根元素div
  • 非Prop的Attribute
    当我们传递给一个组件某个属性,但是该属性并没有定义对应的props或者emits时,就称之为非Prop的 Attribute;常见的包括class、style、id属性等。
  • Attribute继承
    当子组件有单个根节点时,非Prop的Attribute将自动添加到根节点的Attribute中:
<!--父组件:给子组件传了个class属性-->
<HelloWorld title="is title" :banner="bannerMsg" class="bannerWrap" />

<!--子组件渲染出来的DOM:
因为子组件中有根元素div,所以直接给div加了个class属性-->
<div id="app" data-v-app="">
	<div class="bannerWrap">
		<h1>is title</h1>
		<h1>我是banner</h1>
	</div>
</div>
  • 禁用 Attributes 继承
    如果我们不希望组件的根元素继承attribute,可以在子组件中export default中设置inheritAttrs: false
    禁用attribute继承的常见情况是需要将attribute应用于根元素之外的其他元素,可以通过$attrs来访问所有的非props的attribute;
<!--子组件:需要给banner的h1添加class-->
<template>
  <div>
    <h1>{{ title }}</h1>
    <h1 :class="$attrs.class">{{ banner }}</h1>
  </div>
</template>

<!--子组件渲染出来的DOM-->
<div id="app" data-v-app="">
	<div>
		<h1>is title</h1>
		<h1 class="bannerWrap">我是banner</h1>
	</div>
</div>
  • 多根节点的 Attributes 继承
<!--父组件:有多个属性class、id需要传给子组件-->
<HelloWorld title="is title" :banner="bannerMsg" class="bannerWrap" id="bannerWrap"/>

<!--子组件写法,将所有的值都绑定过来-->  
<template>
  <div>
    <h1>{{ title }}</h1>
    <h1 v-bind="$attrs">{{ banner }}</h1>
  </div>
</template>

<!--子组件渲染出来的DOM-->
<div id="app" data-v-app="">
	<div>
		<h1>is title</h1>
		<h1 class="bannerWrap" id="bannerWrap">我是banner</h1>
	</div>
</div>

2、子传父:通过$emit触发事件

  • 什么情况下子组件需要传递内容到父组件呢?
    当子组件有一些内容想要传递给父组件的时候;
    当子组件有一些事件发生的时候,比如在组件中发生了点击,父组件需要切换内容;
  • 操作步骤:
    首先,在子组件中定义好在某些情况下触发的事件名称;
    其次,在父组件中以v-on的方式传入要监听的事件名称,并且绑定到对应的方法中;
    最后,在子组件中发生某个事件的时候,根据事件名称触发对应的事件。
<!--父组件-->
<template>
  <h1>当前数字:{{ counter }}</h1>
  <HelloWorld @add="addNumber" @sub="subNumber" @addN="addNnum" />
</template>

<script>
import HelloWorld from "./components/HelloWorld.vue";

export default {
  name: "App",
  components: {
    HelloWorld,
  },
  data() {
    return {
      counter: 0,
    };
  },
  methods: {
    addNumber() {
      this.counter++;
    },
    subNumber() {
      this.counter--;
    },
    addNnum(num, name, age) {
      this.counter += num;
      console.log(name,age);
    },
  },
};
</script>

<style></style>

<!--子组件:内部其实是监听两个按钮的点击,点击之后通过this.$emit的方式发出去事件-->
<template>
  <div>
    <button @click="increment">+</button>
    <button @click="decrement">-</button>
    
    <input type="text" v-model.number="num" />
    <button @click="incrementN">+n</button>
  </div>
</template>

<script>
export default {
  name: "HelloWorld",
  //不写这个好像也可以
  emits: ["add", "sub"],
  //可以写成对象的形式;对象写法的目的是为了对传递的参数进行验证
  /*emits: {
		add: null,
		sub: null,
		addN: (num, name, age) => {
      console.log(num, name, age);
      if (num > 10) {
        return true;
      }
      return false;
    },
	},*/
  data() {
    return {
      num: 0,
    };
  },
  methods: {
    increment() {
      console.log("+1");
      this.$emit("add");
    },
    decrement() {
      console.log("-1");
      this.$emit("sub");
    },
    ,
    incrementN() {
      console.log("+n");
      //可以传递多个参数
      this.$emit("addN", this.num, "why", 18);
    }
  },
};
</script>

<style scoped></style>

3.4.2. 非父子组件的通信

1、Provide依赖/Inject注入(用于非父子组件之间共享数据)
父组件有一个provide选项来提供数据;
子组件有一个inject选项来开始使用这些数据;

<!--父组件-->
<template>
  <hello-world></hello-world>
</template>

<script>
import HelloWorld from "./components/HelloWorld.vue";
import { computed } from 'vue'

export default {
  name: "App",
  components: {
    HelloWorld,
  },
  /*provide: {
    name: "why",
    age: 18,
  },*/
  //如果Provide中提供的一些数据是来自data,那么我们可能会想要通过this来获取,这个时候需要将provide写成函数的形式并返回
  provide() {
    return {
      name: "why",
      age: 18,
      //如果修改了names数据,不是响应式的,可通过computed函数
      //length: this.names.length,
      length: computed(() => this.names.length)
    };
  },
  data() {
    return {
      names: ["haha", "hehe", "xixi"],
    };
  },
};
</script>

<style></style>

<!--孙组件-->
<template>
	<!--因为computed返回的是一个ref对象,需要取出其中的value来使用-->
  <div>HelloContent: {{ name }} {{ age }} {{ length.value }}</div>
</template>

<script>
export default {
  name: "HelloContent",
  inject: ["name", "age", "length"],
};
</script>

<style scoped></style>

2、全局事件总线eventBus:第三方Mitt库
Vue3从实例中移除了$on$off$once方法
①安装npm install mitt
②封装一个工具(创建)eventbus.js

import mitt from 'mitt';

const emitter = mitt();
// export const emitter1 = mitt();
// export const emitter2 = mitt();

export default emitter;

③使用事件总线工具
在这里插入图片描述

④Mitt的事件取消

//取消emitter中所有的监听
emitter.all.clear()

//定义一个函数
function onFonn(){}
emitter.on('foo', onFoo) //监听
emitter.off('foo', onFoo) //取消监听

3、Vuex(用的最多)

3.5. 插槽

3.6. 动态组件的使用

3.7. 异步组件的使用

3.8. ref引用元素和组件

3.9. 生命周期

3.10. 组件的v-model

三、Vue Cli详解

四、Vue3核心语法

4.1 setup(){}函数

4.1.1 setup(){}的参数

setup接收两个参数props和context
①props是一个对象,它其实就是父组件传递过来的属性会被放到props对象中,可以通过props参数获取。在template中依然是可以正常去使用props中的属性;如果我们在setup函数中想要使用props,那么不可以通过this去获取;因为props有直接作为参数传递到setup函数中,所以我们可以直接通过参数来使用即可。
②context它里面包含三个属性:
attrs:所有的非prop的attribute;
slots:父组件传递过来的插槽;
emit:当我们组件内部需要发出事件时会用到emit(因为不能访问this,所以不可以通过 this.$emit发出事件);

4.1.2 setup(){}的返回值

①setup的返回值可以在模板template中被使用;也就是说我们可以通过setup的返回值来替代data选项;甚至是我们可以返回一个执行函数来代替在methods中定义的方法;这些操作不是响应式的;setup是同步的;
②setup是处于beforecreate和created生命周期间的函数,所以setup在被调用之前是无法使用data、computed、methods等,所以为了避免错误使用,vue将setup函数中的this转化成undefined;
③setup函数中定义的变量和方法都是需要return出去的,不然没有办法在模板中使用;

4.2 reactive

如果想为在setup中定义的数据提供响应式的特性,那么我们可以使用reactive的函数,因为当我们使用reactive函数处理我们的数据之后,数据再次被使用时就会进行依赖收集;当数据发生改变时,所有收集到的依赖都是进行对应的响应式操作(比如更新界面);事实上,我们编写的data选项,也是在内部交给了reactive函数将其编程响应式对象的;

4.3 ref

①reactive API对传入的类型是有限制的,它要求我们必须传入的是一个对象或者数组类型:如果我们传入一个基本数据类型(String、Number、Boolean)会报一个警告;
②ref 会返回一个可变的响应式对象,该对象作为一个响应式的引用维护着它内部的值,这就是ref名称的来源;它内部的值是在ref的value属性中被维护的;
③在模板中引入ref的值时,Vue会自动帮助我们进行解包操作,所以我们并不需要在模板中通过ref.value的方式来使用;但是在setup函数内部,它依然是一个ref引用, 所以对其进行操作时,我们依然需要使用ref.value的方式。

4.4 defineComponent({})

①默认的export default {},对于编辑器而言,{}只是一个Object的类型,无法有针对性的提示vue组件 {} 里应该有哪些属性。
② 它并没有实现任何的逻辑,只是把接收的Object直接返回,它的存在是完全让传入的整个对象获得对应的类型,最主要的功能是为了TypeScript下的类型推导。
③只是对setup函数进行封装,返回options的对象。
使用方法:

import { defineComponent } from 'vue'
export default defineComponent({ ... })

五、vue-router路由

六、Vuex状态管理

七、TypeScript

八、自动化部署

全局拦截器是每个实例都有的拦截器,只有对应的实例才有的拦截器,某一个请求的拦截器

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值