Vue笔记 01

80 阅读7分钟

前言

技术栈

  • 使用vue-cli 创建工程化Vue项目
  • 熟练使用vue指令
  • 熟练封装和使用axios来请求后端的API接口
  • 熟练使用vue-router实现SPA的开发
  • 能够使用Vant, Element UI等组件库, 结合业务需求完成开发

前端工程化

  1. 模块化
  2. 组件化
  3. 规范化
  4. 自动化

什么是 DOM?

文档对象模型 (DOM) 是HTML和XML文档的编程接口。它提供了对文档的结构化的表述,并定义了一种方式可以使程序对该结构进行访问,从而改变文档的结构,样式和内容。DOM 将文档解析为一个由节点和对象(包含属性和方法的对象)组成的结构集合。简言之,它会将web页面和脚本或程序语言连接起来

一个web页面是一个文档。这个文档可以在浏览器窗口或作为HTML源码显示出来。但上述两个情况中都是同一份文档。文档对象模型(DOM)提供了对同一份文档的另一种表现,存储和操作的方式。 DOM是web页面的完全的面向对象表述,它能够使用如 JavaScript等脚本语言进行修改

简介

定义: Vue是一套用于构建用户界面的前端框架

遵循 UI = f(state) 的设计哲学

用户界面的构建历史

  • 构建用户界面1.0

    • 编写结构:基于HTML超文本标记语言
    • 美化样式:基于CSS
    • 处理交互:基于JS操作网页中的DOM对象,处理用户和页面的交互行为
  • 构建用户界面2.0

    • 基于JQuery+模板引擎
  • 构建用户界面3.0:开发者把精力放在核心业务的实现上,而不是操作DOM上

    • 编写结构:基于vue中的指令,方便快捷的渲染页面结构,数据驱动视图(页面依赖的数据源变化则自动渲染)
    • 美化样式:基于CSS
    • 处理交互:基于vue的事件绑定机制,轻松处理用户和页面的交互行为

前端框架

vue提供构建用户界面的一整套解决方案

  1. vue核心库
  2. vue-router路由方案
  3. vuex状态管理方案
  4. vue组件库

辅助vue项目开发的一系列工具

  1. vue-cli(npm全局包,基于webpack,大而全)
  2. vite(npm全局包,小而巧)
  3. vue-devtools(浏览器调试工具)
  4. vetur(vscode插件:提供语法高亮和提示)

Vue的特性

  1. 数据驱动视图(单向数据绑定)

    • 数据的变化会驱动视图自动更新
    • 程序员只需要维护好数据,页面结构会被Vue的底层逻辑, 根据数据自动渲染出来
  2. 双向数据绑定

    • js数据的变化会被自动渲染到页面上
    • 页面上表单的数据变化时会自动同步到js数据中
    • 作用:填写表单时,在不操作DOM的前提下,自动把用户填写的内容同步到数据源
  3. vue组件的特点:

    • 先编译整个页面结构(执行js),再决定要渲染哪一个标签
    • 所有的 DOM 操作都由 Vue 来处理,你编写的代码只需要关注逻辑层面即可。
  4. MVVM

    1. View:当前页面所渲染的DOM结构
    2. Model:当前页面渲染时依赖的数据源
    3. ViewModel:也即Vue实例对象,是MVVM的核心 ,用于把当前页面的数据源(Model)和页面结构(View)连接在了一起, 基于"发布订阅"的设计模式

Vue的版本

  1. Vue2

  2. Vue3

    1. 组合式API,多根节点组件,更好的Ts支持
    2. 废弃了过滤器节点,不支持$on, $off, $once

指令

vue指令可以看作html标签的特殊属性,这样方便理解

vue指令中可以写js代码,进行简单的判断、运算等等

vue指令中写的是js代码,要遵守js的语法规范,但是只能写简单的js表达式,不能写复杂的js语句

1.内容渲染指令——{{}}

辅助开发者渲染DOM元素的文本内容

  1. Mustache插值表达式: 使用{{}}作为内容的占位符,常用

    1. {{}}内部可以渲染JS表达式
    2. :id=" 'stu' - list.number ",属性值内部也可以渲染JS表达式,案例的渲染值为:id="stu-1"
  2. v-text : 会默认覆盖元素内部的值,不常用

  3. v-html:可以把带有html标签的字符串渲染为真正的DOM结构

2.属性绑定指令——:

解决插值表达式只能用在元素的内容节点中,无法动态绑定属性的不足

  • v-bind:

  • 简写为:

  • 属性绑定期间,如果绑定的内容需要进行动态拼接,字符串的外面应该包裹单引号

     <div :title="'box'+index">1段文本</div>
    

3.事件绑定指令——@click/@input/@keyup

v-on:click="事件处理函数",简写为@click="事件处理函数"

  1. 事件对象

    1. 原生DOM事件绑定中,可以在事件处理函数的形参处接收事件对象event,同理
    2. v-on:/@所绑定的事件处理函数中,同样可以接收事件对象event
    3. $event可以解决事件参数对象被实际参数覆盖的问题,只需在标签的事件处理函数中占位即可
    4.  <button @click="add(2, $event)">点击加2</button>
       Vue.createAPP({
         data(){
           return{
             num : 1;
           }
         },
         methods:{
               add(n, e){//点击时,改变按钮背景颜色
              const nowBgColor = e.target.style.backgroundColor;
              e.target.style.backgroundColor = nowBgColor === 'green' ? '' : 'green' 
                   this.num += n;
               }
           }
       })
      
  2. 事件修饰符

    事件处理函数中preventDefalut和stopPropagation是常见需求,事件修饰符辅助我们更方便的对事件的触发进行控制

    1. @click.prevent : 阻止事件的默认行为,例如submit的默认刷新行为,a链接的默认跳转行为
    2. @click.stop : 阻止事件冒泡---(有时在一个元素中包含了子元素,而且父元素和子元素都有点击事件,此时我们希望的点击效果是:点击子元素区域的的时候,不触发父级元素的点击事件)
    3. @click.capture以捕获模式触发当前的事件处理函数
    4. @click.once绑定的事件只触发一次
    5. @click.self只有在event.target是当前元素自身时才触发事件处理函数
  3. 按键修饰符

     <input type="text" @keyup.enter="submit" @keyup.esc="clearInput" />
     methods: {
       clearInput(e) {
         e.target.value = "";
       },
       submit(e){//按下enter,就发送数据
         let value = e.target.value;//获取到当前input输入框内的值
         //将value通过axios提交到数据库
       },
     }
    

4.双向绑定指令——v-model

v-model=""辅助开发者在不操作DOM的前提下,快速获取表单的数据

v-model只能配合表单元素使用

   <input type="text" v-model="username" />
   <hr />
   <p>选中的省份是:{{province}}</p>
   <select v-model="province">
     <option value="">请选择省份</option>
     <option value="1">北京</option>
     <option value="2">河北</option>
     <option value="3">黑龙江</option>
   </select>
 data(){
   return{
     username:'Bart',
     province:'HeBei'
   }
 }
  1. 单向数据绑定

    • 数据源的变动会同步到页面上,但页面的变化不会同步到数据源上
  2. 双向数据绑定v-model

    • 只有表单元素使用双向数据绑定才有意义

      • input输入框

        • type="radio"
        • type="checkbox"
      • textarea

      • select

    • v-model的修饰符

      1. v-model.number - 输入值自动转化为数字

      2. v-model.trim - 自动过滤首尾的空白字符

         <input type="text" v-model.trim="age" />
         <button @click="submitAge">提交</button>
         methods: {
           submitAge() {
             console.log(`用户年龄${this.age}`);
           },
         },
        
      3. v-model.lazy : 阻止即时更新,中间的变化过程不会同步到数据源中——失去焦点时才更新数据

  3. 组件的v-model指令

    作用:维护组件内外数据的同步

    外界数据(根组件中的数据)的变化会被同步到组件中

    组件中数据的变化也会同步到外界(根组件中的数据)

    步骤:

    1. 父组件->子组件

      1. 父组件中通过:属性绑定的形式,把数据传递给子组件
      2. 子组件中通过props接收父组件传递过来的数据
    2. 子组件向父组件

      1. :属性绑定指令前添加v-model指令
      2. 在子组件中声明emits自定义事件,格式为:update:xxx
      3. 调用$emit触发自定义事件,更新父组件中的数据

5.条件渲染指令——v-if-else

实际开发不用考虑性能,二者都可以

  1. v-if=""

    • 通过每次动态创建或移除元素——来实现元素的展示与隐藏

    • 有更高的切换开销

    • 使用条件:刚进来页面时某些元素默认不需要被展示,而且后期也很有可能不被展示

    • 指令值一般是Boolean类型

    • v-if,v-else-if,v-else

       <div v-if="type==='A'">优秀</div>
       <div v-else-if="type==='B'">良好</div>
       <div v-else-if="type==='C'">一般</div>
       <div v-else>较差</div>
       data(){
         return{
           type:"A";
         }
       }
      
  2. v-show=""

    1. 有更高的初始渲染开销
    2. 通过添加和删除display:none——来实现元素的展示与隐藏
    3. 使用条件:频繁切换元素的显示和隐藏状态时使用

6.列表渲染指令——v-for

循环创建谁,就为谁添加v-for指令,常用于列表的li标签中

<li v-for="(item, index) in list" :key="item.id"><li>

官方建议,使用v-for指令时,一定要绑定一个:key属性

使用key属性维护列表的状态

列表的数据变化时,默认情况下vue会尽可能的复用已存在的DOM元素,从而提升渲染的性能。

但这种默认的性能优化策略,会导致有状态的列表(例如复选框<input type="checkbox">)无法被正确更新

为了给vue一个提示,以便它能跟踪每个节点的身份,我们要为每一项提供一个唯一的key属性

  1. key的值类型有要求:字符串或者数字类型
  2. key值必须具有唯一性(key的值不能重复):尽量把id作为key的值
  3. index的值当作key的值没有任何意义:index的值不具有唯一性
  4. 使用v-for时一定要指定key的值:既提升性能,又防止列表状态紊乱
 <table class="table-striped table-hover table-bordered">
     <thead>
       <th>序号</th>
       <th>姓名</th>
       <th>年龄</th>
     </thead>
     <tbody>
       //官方建议,使用v-for指令时,一定要绑定一个:key属性
       //尽量把id作为key的值
       //key的值类型有要求:字符串或者数字类型
       //key值不能重复,否则终端报错:Duplicate keys detected
       <tr v-for="(item, index) in list" :key="item.id">
         <td>{{index+1}}</td>
         <td>{{item.name}}</td>
         <td>{{item.age}}</td>
       </tr>
     </tbody>
 </table>

过滤器

用于文本格式化

1.在插值表达式{{}}中通过管道符|调用过滤器函数

2.在v-bind:=""中通过管道符|调用过滤器函数

定义

  1. 过滤器函数,必须被定义在filters节点之下

  2. 本质上是函数,且函数必须有返回值

  3. 过滤器函数的形参,永远都是管道符前面的值

  4. 分类

    1. 私有过滤器:定义在vue实例中的filters里

    2. 全局过滤器:定义在script标签里,实际开发中常用

       //Vue.filter(全局过滤器的名字,过滤器的处理函数)
       Vue.filter('capitalize',str=>{
         return str.charAt(0)+str.slice(1)
       })//全局定义首字母大写的过滤器函数capitalize
      
    3. 若全局过滤器和私有过滤器重名,私有过滤器优先级更高

  5. 过滤器可以连续调用{{msg | capitalize | maxLength}}

  6. 过滤器可以传参

    Vue.filter('capitalize',function(item, arg1, arg2){
    	//过滤器语法
        return ...
    })
    

案例

(连续调用+传参)

<div id="app">
  <p :title="info | capitalize">{{message | capitalize | maxLength(3)}}</p>
</div>

<script src="./lib/vue-2.6.12.js"></script>
<script>
  // 全局过滤器
  // 首字母转大写的过滤器
  Vue.filter('capitalize', (str) => {
    return str.charAt(0).toUpperCase() + str.slice(1)
  })

  // 定义控制文本长度的过滤器
  Vue.filter('maxLength', (str, len = 10) => {
    if(str.length <= len) return str
    return str.slice(0, len) + '...'
  })
</script>

(时间格式化)

<script src="https://unpkg.com/dayjs@1.8.21/dayjs.min.js"></script>

Vue.filter("dateFormat", (time) =>
dayjs(time).format("YYYY-MM-DD-HH:mm:ss")
);

组件的节点

  1. name——组件名称

    1. 短横线命名法:my-component
    2. 帕斯卡命名法:MyComponent
  2. components——局部注册的子组件名称

  3. data——数据源:是MVVM里的Model

  4. methods——方法

  5. props——自定义属性:方便组件的使用者为组件提供数据,提高组件可复用性

  6. computed——计算属性

  7. emits自定义事件:方便组件的使用者监听子组件的数据变化

  8. watch——侦听器

  9. filter——过滤器,过滤器函数必须要有返回值

    vue3中不支持,建议使用计算属性或方法代替过滤器功能

    filters:{
    	capitalize(s){
    		return something;
    	}
    }
    
  10. 待定

SPA

定义:一个web网站内只有唯一的一个HTML页面,所有的功能与交互都是基于这个页面呈现的

特点:

  1. 仅在web页面初始化时加载相应资源(前端三件套)
  2. 一旦页面加载完成,SPA不会因为用户的操作而进行页面的重新加载跳转,而是利用js动态变换html的内容,实现页面与用户的交互

优点:

  1. 良好的交互体验

    1. 改变无需重新加载整个页面
    2. 获取数据通过Ajax异步获取
    3. 没有页面之间的跳转,没有白屏
  2. 良好的前后端分离模式

    1. 后端专注提供API接口
    2. 前端专注于页面的渲染
  3. 减轻服务器的压力

    1. 服务器只是提供数据,不负责页面的合成于逻辑的处理,吞吐能力大幅度提高

缺点:

  1. 首屏加载慢

    1. 路由懒加载
    2. 代码压缩
    3. CDN加速
    4. 网络传输压缩
  2. 不利于SEO

    1. SSR服务端渲染

创建工程化的Vue项目

  1. vite-学习用,仅支持vue3

    npm init vite-app demo1
    cd demo1
    npm i
    npm run dev
    

    项目组成:

    1. node_modules——第三方依赖

    2. public——公开的静态资源目录

    3. src——项目的源代码目录

      • assets——静态资源文件(css,fonts)

      • components——自定义组件

      • App.vue——项目根组件

      • index.css——全局样式表

      • main.js——项目的打包入口文件

        import {createApp} from 'vue'//按需导入vue实例的构造函数
        import App from './App.vue'
        import MyComponent from "./components/Test.vue"
        const app = createApp(App)
        app.component('my-component',组件名)//这里是全局注册,注意和局部注册区分
        app.mount("#app")
        
    4. .gitignore——git的忽略文件

    5. index.html——SPA中唯一的HTML页面

    6. package.json——项目的包管理配置文件

  2. vue/cli-企业开发用

VUE项目的运行流程

通过main.js打包入口文件把App.vue渲染到index.html的指定区域

组件化开发:

  1. 把页面上可以复用的部分封装为组件
  2. 提高复用性
  3. 加快开发效率

组件

1.构成

  1. template模板结构,没有实际意义,渲染时不会作为页面的标签

  2. script行为

  3. <style lang='less' scoped>样式——less语法实现嵌套css样式的效果

    npm i less -D

2.注册

导入——>注册——>使用

全局注册

main.js打包入口文件中注册

import Swiper from "./components/MySwiper.vue"//此时的Swiper就是组件导入进来的名称
import App from "./App.vue"
const app = createApp(App)
app.component('my-swiper', Swiper)
//app.component('组件的使用名称',组件导入进来的名称)

局部注册

在对应要注册的组件.vue文件下

导入——>注册——>使用

3.组件样式冲突问题

  1. 通过自定义属性data-v-xxx
<template>
	<div data-v-001>
		<h1 data-v-001>Hello</h1>
		<p data-v-001>123</p>
	</div>
</template>
<style>
	h1[data-v-001]{
		color:blue;
	}
</style>

style标签添加scoped属性可以达到相同的效果<style scoped>

  1. deep样式穿透

    作用:当前组件设置scoped后,默认其样式对子组件是不生效的。如果想要让组件的某些样式对子组件生效,可以在样式前添加/deep/深度选择器,vue3中使用:deep(<inner-selector>)

4.Class与Style绑定

实际开发中,遇到动态操作元素样式的需求,因此,使用v-bind属性绑定指令,为元素动态绑定class属性的值和行内的style样式

动态绑定HTML的class

  1. 三元表达式

<h1 :class="isItalic ? 'Italic' : ''">

where data(){ return {isItalic:true} }——可以通过修改data中的数据,改变h1的class属性,达到修改css样式的效果

  1. 数组形式

<h1:class="[isItalic ? 'Italic' : '', isDelete ? 'Delete' : '']">

  1. 以对象语法绑定HTML的class

    <h1 :class="classObj">

    classObj:{

    isItalic : false,

    isDelete : false

    }

    <style>isItalic{font:''},isDelete:{display:none}</style>

  2. 以对象语法绑定内联的style

5.父子组件传值⭐

又称为组件的v-model指令

作用:维护组件内外数据的同步

作用:

  1. 外界数据的变化会被同步到组件中
  2. 组件中数据的变化也会同步到外界

步骤:

  1. 父组件->子组件

    1. 父组件中通过:属性绑定的形式,把数据传递给子组件
    2. 子组件中通过props接收父组件传递过来的数据
  2. 子组件向父组件

    1. :属性绑定指令前添加v-model指令

    2. 在子组件中emits节点中定义自定义事件,格式为:update:xxx

      emits:['update:要更新的属性值']
      
    3. 调用$emit触发自定义事件,更新父组件中的数据

Props

自定义属性

props是只读的,无法修改

1.props基础

封装组件时要注意:

  1. 组件的DOM结构,style样式要尽量复用
  2. 组件中展示的数据由组件的使用者提供

Vue提供props自定义属性节点,方便使用者为组件提供要展示的数据,增强组件的复用性

定义:

组件的使用者可以通过props把数据传递到子组件的内部,供子组件内部使用

使用方法:

  1. 数组类型

    props: ['name','age'],
    
  2. 对象类型

    props: init:{},init1:{},init2:{}
    

动态绑定props的值:

可以使用v-bind:属性绑定指令,为组件动态绑定props的值

<MyComponent :name='student1.name', :age='student1.age' v-for="(student, index) in list"></MyComponent>
props: ['name','age'],

细节:

  1. props 是"自定义属性",允许使用者通过自定义属性,为当前组件指定初始值
  2. 自定义属性的名字,是封装者自定义的(只要名称合法即可)
  3. props 中的数据,可以直接在模板结构中被使用
  4. props 是只读的,不要直接修改 props 的值,否则终端会报错!

2.props验证

在封装组件时对外界传递的props数据进行合法校验,防止数据不合法的问题

type为类型

required为必填项校验

default为默认值

validator为自定义验证函数

//props: ["info","age"]
props:{
	info : {
		type:[String, Number],
		required : true,
		default : '张三',
		validator(value){
			//不等于-1说明value值在数组里
			return ['success','warning','danger'].indexOf(value) !== -1
		}
	},
	age : Number
}

computed

计算属性

本质是一个function函数,可以实时监听data中数据的变换,并return一个计算后的值,供组件渲染DOM时使用

会缓存计算结果,性能比方法更好

必须有一个返回值

  1. 计算属性是一个function函数,被定义在computed:{}节点下
  2. 计算属性的使用方法和data中的数据一样
  3. 会自动渲染求值,减少了代码复用
  4. function里的this和箭头函数中不同

相对于methods方法,计算属性会缓存计算结果,只有计算属性的依赖项变化时,才会重新运算,所以计算属性的性能更好

emits节点

作用:让组件的使用者可以监听到组件内状态的变化

使用步骤

  1. 封装组件时

    • 声明自定义事件:自定义事件声明到emits数组中
    • 触发自定义事件:this.$emit('自定义事件的名称',要传递的val)
  2. 使用组件时

    • 监听自定义事件:@的形式的形式监听自定义事件

      //使用组件时
      <myEvent @自定义事件的名称="处理事件"></myEvent>
      处理事件(val){
      	do(val);
      }
      

watch

用来监听数据变化

定义在watch节点之下

本质是函数,把想要监听的数据名作为侦听器的函数名

案例:检测用户名是否被占用

//script引入jquery-v3.6.0.js
<script src="/lib/jquery-v3.6.0.js"></script>
<script>
const vm = new Vue({
    el: "#app",
    data:{
        username: "";
    },
    watch:{
        username(newVal, oldVal){
            if(newVal==='') return
            //调用jQuery中的Ajax发起请求,并判断newVal是否被占用
            $.get("https://www.escook.cn/api/finduser/" + newVal, function(result){
                console.log(result);
            })
        }
    }
})
</script>

侦听器的格式

  1. 方法格式的侦听器(优先定义为方法格式)

    • 缺点1:无法在刚进入页面的时候自动触发
    • 缺点2:如果侦听的是一个对象,当对象的属性变化时不会触发侦听器
  2. 对象格式的侦听器

    • 可以通过immediate选项,让侦听器自动触发:immediate选项的默认值是false,设置为true后在进入页面的时候会自动触发侦听器函数

    • 可以通过deep选项,让侦听器侦听对象中每个属性的变化

      data:{
          info:{
              username: "Lucy"
          }
      },  
      watch: {
          // 如果要侦听的是对象的子属性的变化,则必须包裹一层单引号
          'info.username'(newVal) {
            console.log(newVal)
          }
        }
      

生命周期⭐

关注的是时间点,不是时间段

创建阶段

  1. beforeCreate

  2. created

    1. 组件的props/data/methods都已创建好
    2. 调用Ajax获取数据,加载到data里
    3. 核心:数据渲染完成
  3. beforeMount

  4. mounted

    1. 内存中的HTML结构,已经渲染到浏览器中
    2. 操作DOM元素
    3. 核心:DOM渲染完成

运行阶段

  1. beforeUpdate

    1. props/data已更新
    2. HTML模板结构未渲染
  2. updated

    1. props/data已更新
    2. HTML模板结构已渲染
    3. 已经根据最新数据完成了DOM结构的重新渲染

销毁阶段