2025年Vue框架

Vue框架一 Vue 框架概述 1 vue 框架的介绍 渐进式 javacript 框架 一套拥有自己规则的语法 Vue 是一套用于构建用户界面的渐进式框架 与其它大型框架不同的是 Vue 被设计为可以自底向上逐层应用 Vue js 是前端的主流框架之一 和 Angular js React js 一起 并成为前端三大主流框架

大家好,我是讯享网,很高兴认识大家。

一、Vue框架概述

1. vue框架的介绍

渐进式javacript框架, 一套拥有自己规则的语法

Vue 是一套用于构建用户界面的渐进式框架. 与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。

Vue.js 是前端的主流框架之一,和 Angular.js、React.js 一起,并成为前端三大主流框架

官网地址: Vue.js - 渐进式 JavaScript 框架 | Vue.jsVue.js - 渐进式的 JavaScript 框架
讯享网https://cn.vuejs.org/
(作者: 尤雨溪)

2. vue的特点

  • 渐进式
  • 声明式渲染
  • 数据驱动视图 (响应式)
  • 极少的去写DOM操作相关代码
  • 双向绑定
  • 组件系统
  • 不兼容IE8及以下浏览器

3. Vue的优点

1.体积小  2.更高的运行效率 3.双向数据绑定,简化 Dom 操作 通过 MVVM 思想实现数据的双向绑定 4 生态丰富、学习成本低 市场上拥有大量成熟、稳定的基于 vue.js 的 ui 框架、常用组件 来即用实现 快速开发 对初学者友好、入门容易、学习资料多.

讯享网

二、@vue/cli脚手架

1. @vue/cli 脚手架介绍

      @vue/cli是Vue官方提供的一个全局模块包(得到vue命令), 此包用于创建脚手架项目

脚手架是为了保证各施工过程顺利进行而搭设的工作平台

@vue/cli的好处

                             开箱即用

                             0配置webpack

                             babel支持

                             css, less支持

                             开发服务器支持

2. @vue/cli安装

目标: 把@vue/cli模块包按到全局, 电脑拥有vue命令, 才能创建脚手架工程

  • (1)全局安装命令

讯享网yarn global add @vue/cli # OR npm install -g @vue/cli

注意: 如果半天没动静(95%都是网速问题), 可以ctrl c停止重新来,换一个网继续重来

  • (2)查看vue脚手架版本

    vue -V

总结: 如果出现版本号就安装成功, 否则失败

3. @vue/cli 创建项目启动服务

目标: 使用vue命令, 创建脚手架项目

(1)创建项目

讯享网# vue和create是命令, vuecli-demo是文件夹名 vue create vuecli-demo

注意: 项目名不能带大写字母, 中文和特殊符号

(2)选择模板和包管理器, 等待脚手架项目创建完毕

a. 选择预置版本为vue2还是vue3(可以上下箭头选择, 弄错了ctrl+c重来)

b. 选择用什么方式下载脚手架项目需要的依赖包  

c. 回车等待生成项目文件夹+文件+下载必须的第三方包们  

 d. cd进入项目下, 启动内置的webpack本地热更新开发服务器
cd vuecil-demo ​ npm run serve # 或 yarn serve
讯享网只要看到绿色成功了(底层node+webpack热更新服务)

 e. 如果未自动弹出浏览器, 手动打开浏览器输入上述地址

4.  @vue/cli 目录和代码分析

vuecil-demo        # 项目目录
    ├── node_modules # 项目依赖的第三方包
    ├── public       # 静态文件目录
      ├── favicon.ico# 浏览器小图标
      └── index.html # 单页面的html文件(网页浏览的是它)
    ├── src          # 业务文件夹
      ├── assets     # 静态资源
        └── logo.png # vue的logo图片
      ├── components # 组件目录
        └── HelloWorld.vue # 欢迎页面vue代码文件 
      ├── App.vue    # 整个应用的根组件
      └── main.js    # 入口js文件
    ├── .gitignore   # git提交忽略配置
    ├── babel.config.js  # babel配置
    ├── package.json  # 依赖包列表
    ├── README.md    # 项目说明
    └── yarn.lock    # 项目包版本锁定和缓存地址

主要文件及含义

讯享网node_modules下都是下载的第三方包
public/index.html – 浏览器运行的网页
src/main.js – webpack打包的入口文件
src/App.vue – vue项目入口页面
package.json – 依赖包列表文件

5. @vue/cli 项目架构了解

 

6. @vue/cli 自定义配置

目标:项目中没有webpack.config.js文件,因为@vue/cli用的vue.config.js

src并列处新建vue.config.js

/* 覆盖webpack的配置 */ module.exports = {  devServer: { // 自定义服务配置    open: true, // 自动打开浏览器    port: 3000 } }

7. eslint了解

目标: 知道eslint的作用, 和如何暂时关闭, 它是一个代码检查工具

(1)如果写代码违反了eslint的规则-报错

演示: 在main.js中随便定义变量 – 不使用 – 观察eslint报错

 (2)处理eslint代码检查

方式1: 手动解决掉错误, 以后项目中会讲如何自动解决

方式2: 暂时关闭eslint检查(因为现在主要精力在学习Vue语法上), 在vue.config.js中配置后重启服务

8. @vue/cli 单vue文件讲解

目标: 单vue文件好处, 独立作用域互不影响

讯享网Vue推荐采用.vue文件来开发项目

template里只能有一个根标签

vue文件-独立模块-作用域互不影响

style配合scoped属性, 保证样式只针对当前template内标签生效

vue文件配合webpack, 把他们打包起来插入到index.html
<!-- template必须, 只能有一个根标签, 影响渲染到页面的标签结构 --> <template>  <div>欢迎使用vue</div> </template> ​ <!-- js相关 --> <script> export default {  name: 'App' } </script> ​ <!-- 当前组件的样式, 设置scoped, 可以保证样式只对当前页面有效 --> <style scoped> </style> ​

最终: Vue文件配合webpack, 把他们打包起来插入到index.html, 然后在浏览器运行

讯享网单vue文件好处? 独立作用域, 不再担心变量重名问题

9. @vue/cli 欢迎界面清理

 

三、vue基础-MVVM设计模式

目的: 转变思维, 用数据驱动视图改变, 操作dom的事, vue源码内操作了

设计模式: 是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。

  • MVVM,一种软件架构模式,决定了写代码的思想和层次
    • M: model数据模型 (data里定义)
    • V: view视图 (html页面)
    • VM: ViewModel视图模型 (vue.js源码)
  • MVVM通过数据双向绑定让数据自动地双向同步 不再需要操作DOM
    • V(修改视图) -> M(数据自动同步)
    • M(修改数据) -> V(视图自动同步)

1. 在vue中,不推荐直接手动操作DOM

2. 在vue中,通过数据驱动视图,不再需操作DOM,而是如何操作数据

总结: vue源码内采用MVVM设计模式思想, 大大减少了DOM操作, 挺高开发效率  

mvvm和mvc区别是什么? MVC: 也是一种设计模式, 组织代码的结构, 是model数据模型, view视图, Controller控制器, 在控制器这层里编写js代码, 来控制数据和视图关联 MVVM: 即Model-View-ViewModel的简写。即模型-视图-视图模型, VM是这个设计模式的核心, 连接v和m的桥梁, 内部会监听DOM事件, 监听数据对象变化来影响对方. 我们称之为数据绑定

四、vue基础

1. 插值表达式

目的: 在dom标签中, 直接插入内容

又叫: 声明式渲染/文本插值

语法: { { 表达式 }}

讯享网msg和obj是vue数据变量 要在js中data函数里声明

<template> <div> <h1>{ 
  
    
  { msg }}</h1> <h2>{ 
  
    
  { obj.name }}</h2> <h3>{ 
  
    
  { obj.age > 18 ? '成年' : '未成年' }}</h3> </div> </template> <script> export default { data() { // 格式固定, 定义vue数据之处 return { // key相当于变量名 msg: "hello, vue", obj: { name: "小vue", age: 5 } } } } </script> <style> </style>

 总结: dom中插值表达式赋值, vue的变量必须在data里声明

 2. vue指令-v-bind

目标: 给标签属性设置vue变量的值

vue指令, 实质上就是特殊的 html 标签属性, 特点: v- 开头

每个指令, 都有独立的作用

  • 语法:v-bind:属性名="vue变量"
  • 简写::属性名="vue变量"
讯享网<!-- vue指令-v-bind属性动态赋值 --> <a v-bind:href="url">我是a标签</a> <img :src="imgSrc">

总结: 把vue变量的值, 赋予给dom属性上, 影响标签显示效果

3. vue指令-v-on

目标: 给标签绑定事件

  • 语法
    • v-on:事件名="要执行的==少量代码=="
    • v-on:事件名="methods中的函数"
    • v-on:事件名="methods中的函数(实参)"
  • 简写: @事件名="methods中的函数"
<!-- vue指令:   v-on事件绑定--> <p>你要买商品的数量: { 
  
    
  {count}}</p> <button v-on:click="count = count + 1">增加1</button> <button v-on:click="addFn">增加1个</button> <button v-on:click="addCountFn(5)">一次加5件</button> ​ <button @click="subFn">减少</button> ​ <script>    export default {        // ...其他省略        methods: {            addFn(){ // this代表export default后面的组件对象(下属有data里return出来的属性)                this.count++           },            addCountFn(num){                this.count += num           },            subFn(){                this.count--           }       }   } </script>

总结: 常用@事件名, 给dom标签绑定事件, 以及=右侧事件处理函数

练习-翻转世界

目标: 点击按钮 - 把文字取反显示 - 再点击取反显示(回来了)

提示: 把字符串取反赋予回去

讯享网<template> <div> <h1>{ 
  
    
  { message }}</h1> <button @click="btn">逆转世界</button> </div> </template> <script> export default { data() { return { message: "HELLO, WORLD", }; }, methods: { btn(){ this.message = this.message.split("").reverse().join("") } } }; </script>

4. vue指令-v-on事件对象

目标: vue事件处理函数中, 拿到事件对象

  • 语法:
    • 无传参, 通过形参直接接收
    • 传参, 通过$event指代事件对象传给事件处理函数
<template>  <div>    <a @click="one" href="http://www.baidu.com">阻止百度</a>    <hr>    <a @click="two(10, $event)" href="http://www.baidu.com">阻止去百度</a>  </div> </template> ​ <script> export default {  methods: {    one(e){      e.preventDefault()   },    two(num, e){      e.preventDefault()   } } } </script>

5. vue指令-v-on修饰符

目的: 在事件后面.修饰符名 - 给事件带来更强大的功能

  • 语法:
    • @事件名.修饰符="methods里函数"
      • .stop - 阻止事件冒泡
      • .prevent - 阻止默认行为
      • .once - 程序运行期间, 只触发一次事件处理函数
讯享网<template>  <div @click="fatherFn">    <!-- vue对事件进行了修饰符设置, 在事件后面.修饰符名即可使用更多的功能 -->    <button @click.stop="btn">.stop阻止事件冒泡</button>    <a href="http://www.baidu.com" @click.prevent="btn">.prevent阻止默认行为</a>    <button @click.once="btn">.once程序运行期间, 只触发一次事件处理函数</button>  </div> </template> ​ <script> export default {  methods: {    fatherFn(){      console.log("father被触发");   },    btn(){      console.log(1);   } } } </script>

总结: 修饰符给事件扩展额外功能

6. vue指令-v-on按键修饰符

目标: 给键盘事件, 添加修饰符, 增强能力

  • 语法:
    • @keyup.enter - 监测回车按键
    • @keyup.esc - 监测返回按键

更多修饰符https://cn.vuejs.org/v2/guide/events.html#%E6%8C%89%E9%94%AE%E4%BF%AE%E9%A5%B0%E7%AC%A6

<template>  <div>    <input type="text" @keydown.enter="enterFn">    <hr>    <input type="text" @keydown.esc="escFn">  </div> </template> ​ <script> export default { methods: {   enterFn(){     console.log("enter回车按键了");   },   escFn(){     console.log("esc按键了");   } } } </script>

总结: 多使用事件修饰符, 可以提高开发效率, 少去自己判断过程

7. vue指令 v-model

目标: 把value属性和vue数据变量, 双向绑定到一起

  • 语法: v-model="vue数据变量"
  • 双向数据绑定
    • 数据变化 -> 视图自动同步
    • 视图变化 -> 数据自动同步

  • 演示: 用户名绑定 - vue内部是MVVM设计模式
讯享网<template>  <div>    <!--   v-model:是实现vuejs变量和表单标签value属性, 双向绑定的指令    -->    <div>      <span>用户名:</span>      <input type="text" v-model="username" />    </div>    <div>      <span>密码:</span>      <input type="password" v-model="pass" />    </div>    <div>      <span>来自于: </span>      <!-- 下拉菜单要绑定在select上 -->      <select v-model="from">        <option value="北京市">北京</option>        <option value="南京市">南京</option>        <option value="天津市">天津</option>      </select>    </div>    <div>      <!-- (重要)      遇到复选框, v-model的变量值      非数组 - 关联的是复选框的checked属性      数组   - 关联的是复选框的value属性       -->      <span>爱好: </span>      <input type="checkbox" v-model="hobby" value="抽烟">抽烟      <input type="checkbox" v-model="hobby" value="喝酒">喝酒      <input type="checkbox" v-model="hobby" value="写代码">写代码    </div>    <div>      <span>性别: </span>      <input type="radio" value="男" name="sex" v-model="gender">男      <input type="radio" value="女" name="sex" v-model="gender">女    </div>    <div>      <span>自我介绍</span>      <textarea v-model="intro"></textarea>    </div>  </div> </template> ​ <script> export default {  data() {    return {      username: "",      pass: "",      from: "",      hobby: [],      sex: "",      intro: "",   };    // 总结:    // 特别注意: v-model, 在input[checkbox]的多选框状态    // 变量为非数组, 则绑定的是checked的属性(true/false) - 常用于: 单个绑定使用    // 变量为数组, 则绑定的是他们的value属性里的值 - 常用于: 收集勾选了哪些值 } }; </script>

总结: 本阶段v-model只能用在表单元素上, 以后学组件后讲v-model高级用法

8. vue指令 v-model修饰符

目标: 让v-model拥有更强大的功能

  • 语法:
    • v-model.修饰符="vue数据变量"
      • .number 以parseFloat转成数字类型
      • .trim 去除首尾空白字符
      • .lazy 在change时触发而非inupt时

<template>  <div>    <div>      <span>年龄:</span>      <input type="text" v-model.number="age">    </div>    <div>      <span>人生格言:</span>      <input type="text" v-model.trim="motto">    </div>    <div>      <span>自我介绍:</span>      <textarea v-model.lazy="intro"></textarea>    </div>  </div> </template> ​ <script> export default {  data() {    return {      age: "",      motto: "",      intro: ""   } } } </script>

总结: v-model修饰符, 可以对值进行预处理, 非常高效好用

9. vue指令 v-text和v-html

目的: 更新DOM对象的innerText/innerHTML

  • 语法:
    • v-text="vue数据变量"
    • v-html="vue数据变量"

  • 注意: 会覆盖插值表达式
讯享网<template>
  <div>
    <p v-text="str"></p>
    <p v-html="str"></p>
  </div>
</template>
​
<script>
export default {
  data() {
    return {
      str: "<span>我是一个span标签</span>"
    }
  }
}
</script>

总结: v-text把值当成普通字符串显示, v-html把值当做html解析

10. vue指令 v-show和v-if

目标: 控制标签的隐藏或出现

  • 语法:
    • v-show="vue变量"
    • v-if="vue变量"

  • 原理
    • v-show 用的display:none隐藏 (频繁切换使用)
    • v-if 直接从DOM树上移除
  • 高级
    • v-else使用
<template>  <div>    <h1 v-show="isOk">v-show的盒子</h1>    <h1 v-if="isOk">v-if的盒子</h1> ​    <div>      <p v-if="age > 18">我成年了</p>      <p v-else>还得多吃饭</p>    </div>  </div> </template> ​ <script> export default {  data() {    return {      isOk: true,      age: 15   } } } </script>

总结: 使用v-show和v-if以及v-else指令, 方便通过变量控制一套标签出现/隐藏

案例-折叠面板

目标: 点击展开或收起时,把内容区域显示或者隐藏

此案例使用了less语法, 项目中下载模块

讯享网yarn add less@3.0.4 less-loader@5.0.0 -D
<template> <div id="app"> <h3>案例:折叠面板</h3> <div> <div class="title"> <h4>芙蓉楼送辛渐</h4> <span class="btn" @click="isShow = !isShow"> { 
  
    
  { isShow ? '收起' : '展开' }} </span> </div> <div class="container" v-show="isShow"> <p>寒雨连江夜入吴, </p> <p>平明送客楚山孤。</p> <p>洛阳亲友如相问,</p> <p>一片冰心在玉壶。</p> </div> </div> </div> </template> <script> export default { data() { return { isShow: false } } } </script> <style lang="less"> body { background-color: #ccc; #app { width: 400px; margin: 20px auto; background-color: #fff; border: 4px solid blueviolet; border-radius: 1em; box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.5); padding: 1em 2em 2em; h3 { text-align: center; } .title { display: flex; justify-content: space-between; align-items: center; border: 1px solid #ccc; padding: 0 1em; } .title h4 { line-height: 2; margin: 0; } .container { border: 1px solid #ccc; padding: 0 1em; } .btn { /* 鼠标改成手的形状 */ cursor: pointer; } } } </style>

11. vue指令-v-for

目标: 列表渲染, 所在标签结构, 按照数据数量, 循环生成

  • 语法
    • v-for="(值, 索引) in 目标结构"
    • v-for="值 in 目标结构"
  • 目标结构:
    • 可以遍历数组 / 对象 / 数字 / 字符串 (可遍历结构)
  • 注意:

    v-for的临时变量名不能用到v-for范围外

讯享网<template>  <div id="app">    <div id="app">      <!-- v-for 把一组数据, 渲染成一组DOM -->      <!-- 口诀: 让谁循环生成, v-for就写谁身上 -->      <p>学生姓名</p>      <ul>        <li v-for="(item, index) in arr" :key="item">          { 
  
    
  { index }} - { 
  
    
  { item }}        </li>      </ul> ​      <p>学生详细信息</p>      <ul>        <li v-for="obj in stuArr" :key="obj.id">          <span>{ 
  
    
  { obj.name }}</span>          <span>{ 
  
    
  { obj.sex }}</span>          <span>{ 
  
    
  { obj.hobby }}</span>        </li>      </ul> ​      <!-- v-for遍历对象(了解) -->      <p>老师信息</p>      <div v-for="(value, key) in tObj" :key="value">        { 
  
    
  { key }} -- { 
  
    
  { value }}      </div> ​      <!-- v-for遍历整数(了解) - 从1开始 -->      <p>序号</p>      <div v-for="i in count" :key="i">{ 
  
    
  { i }}</div>    </div>  </div> </template> ​ <script> export default {  data() {    return {      arr: ["小明", "小欢欢", "大黄"],      stuArr: [       {          id: 1001,          name: "孙悟空",          sex: "男",          hobby: "吃桃子",       },       {          id: 1002,          name: "猪八戒",          sex: "男",          hobby: "背媳妇",       },     ],      tObj: {        name: "小黑",        age: 18,        class: "1期",     },      count: 10,   }; }, }; </script>

总结: vue最常用指令, 铺设页面利器, 快速把数据赋予到相同的dom结构上循环生成

为什么避免v-for和v-if在一起使用 ​ Vue 处理指令时,v-for 比 v-if 具有更高的优先级, 虽然用起来也没报错, 但是性能不高, 如果你有5个元素被v-for循环, v-if也会分别执行5次.

 练习-购物车

目标: 完成商品浏览和删除功能, 当无数据给用户提示

  • 需求1: 根据给的初始数据, 把购物车页面铺设出来
  • 需求2: 点击对应删除按钮, 删除对应数据
  • 需求3: 当数据没有了, 显示一条提示消息
    讯享网<template> <div id="app"> <table class="tb"> <tr> <th>编号</th> <th>品牌名称</th> <th>创立时间</th> <th>操作</th> </tr> <!-- 循环渲染的元素tr --> <tr v-for="(item,index) in list" :key="item.id"> <td>{ 
        
          
        {item.id}}</td> <td>{ 
        
          
        {item.name}}</td> <td>{ 
        
          
        {item.time}}</td> <td> <button @click="del(index)">删除</button> </td> </tr> <tr v-if="list.length === 0"> <td colspan="4">没有数据咯~</td> </tr> </table> </div> </template> <script> export default { data() { return { list: [ { id: 1, name: "奔驰", time: "2020-08-01" }, { id: 2, name: "宝马", time: "2020-08-02" }, { id: 3, name: "奥迪", time: "2020-08-03" }, ], }; }, methods: { del(index) { // 删除按钮 - 得到索引, 删除数组里元素 this.list.splice(index, 1); }, }, }; </script> <style> #app { width: 600px; margin: 10px auto; } .tb { border-collapse: collapse; width: 100%; } .tb th { background-color: #0094ff; color: white; } .tb td, .tb th { padding: 5px; border: 1px solid black; text-align: center; } .add { padding: 5px; border: 1px solid black; margin-bottom: 10px; } </style> 

    12. vue基础 v-for更新监测

    目标: 当v-for遍历的目标结构改变, Vue触发v-for的更新

    情况1: 数组翻转

    情况2: 数组截取

    情况3: 更新值

    口诀:      数组变更方法, 就会导致v-for更新, 页面更新     数组非变更方法, 返回新数组, 就不会导致v-for更新, 可采用覆盖数组或this.$set()

这些方法会触发数组改变, v-for会监测到并更新页面

讯享网push() pop() shift() unshift() splice() sort() reverse()

这些方法不会触发v-for更新

slice() filter() concat()

  

讯享网<template>  <div>    <ul>      <li v-for="(val, index) in arr" :key="index">        { 
  
    
  { val }}      </li>    </ul>    <button @click="revBtn">数组翻转</button>    <button @click="sliceBtn">截取前3个</button>    <button @click="updateBtn">更新第一个元素值</button>  </div> </template> ​ <script> export default {  data(){    return {      arr: [5, 3, 9, 2, 1]   } },  methods: {    revBtn(){      // 1. 数组翻转可以让v-for更新      this.arr.reverse()   },    sliceBtn(){      // 2. 数组slice方法不会造成v-for更新      // slice不会改变原始数组      // this.arr.slice(0, 3) ​      // 解决v-for更新 - 覆盖原始数组      let newArr = this.arr.slice(0, 3)      this.arr = newArr   },    updateBtn(){      // 3. 更新某个值的时候, v-for是监测不到的      // this.arr[0] = 1000; ​      // 解决-this.$set()      // 参数1: 更新目标结构      // 参数2: 更新位置      // 参数3: 更新值      this.$set(this.arr, 0, 1000)   } } } </script> ​ <style> ​ </style>

注意: vue不能监测到数组里赋值的动作而更新, 如果需要请使用Vue.set() 或者this.$set(), 或者覆盖整个数组

总结: 改变原数组的方法才能让v-for更新

13. vue基础 v-for就地更新

v-for 的默认行为会尝试原地修改元素而不是移动它们。

详解v-for就地更新流程

 这种虚拟DOM对比方式(同级对比,标签与标签对比,相同则复用), 可以提高性能 - 但是还不够高

14. vue基础_虚拟dom

(1)真实DOM:在document对象上, 渲染到浏览器上显示的标签

 (2)虚拟DOM:本质是保存节点信息, 属性和内容的一个JS对象

       a.  .vue文件中的template里写的标签, 都是模板, 都要被vue处理成虚拟DOM对象, 才会渲染显示到真实DOM页面上

    b.  内存中生成一样的虚拟DOM结构(本质是个JS对象)

(3)vue数据更新

- 生成新的虚拟DOM结构 - 和旧的虚拟DOM结构对比 - 利用diff算法, 找不不同, 只更新变化的部分(重绘/回流)到页面 - 也叫打补丁

好处1: 提高了更新DOM的性能(不用把页面全删除重新渲染)

好处2: 虚拟DOM只包含必要的属性(没有真实DOM上百个属性)

总结: 虚拟DOM保存在内存中, 只记录dom关键信息, 配合diff算法提高DOM更新的性能

在内存中比较差异, 然后给真实DOM打补丁更新上

15. vue基础_diff算法

vue用diff算法, 新虚拟dom和旧的虚拟dom比较

(1)同级比较-根元素变化-整个dom树删除重建

 情况1: 根元素变了, 删除重建

旧虚拟DOM

讯享网<div id="box">    <p class="my_p">123</p> </div>

新虚拟DOM

<ul id="box">    <li class="my_p">123</li> </ul>

(2)同级比较-根元素不变-属性改变更新属性

情况2: 根元素没变, 属性改变, 元素复用, 更新属性

旧虚拟DOM

讯享网<div id="box">    <p class="my_p">123</p> </div>

新虚拟DOM

<div id="myBox" title="标题">    <p class="my_p">123</p> </div>

16. vue基础_diff算法-key

情况3: 根元素没变, 子元素没变, 元素内容改变

(1)无key - 就地更新(最大限度尝试就地修改/复用相同类型元素)

 

讯享网<ul id="myUL"> <li v-for="str in arr"> { 
  
    
  { str }} <input type="text"> </li> </ul> <button @click="addFn">下标为1的位置新增一个</button>
export default { data(){ return { arr: ["老大", "新来的", "老二", "老三"] } }, methods: { addFn(){ this.arr.splice(1, 0, '新来的') } } };

旧虚拟DOM结构 和 新虚拟DOM结构 对比过程  

性能不高, 从第二个li往后都更新了  

(2)有key - 值为索引(还是就地更新 )

有key属性, 基于key的来比较新旧虚拟DOM, 移除key不存在元素

        先产生新旧虚拟DOM, 根据key比较, 因为新旧虚拟DOM对比, key存在就复用此标签更新内容, 如果不存在就直接建立一个新的,因此还是就地更新

讯享网<ul id="myUL"> <li v-for="(str, index) in arr" :key="index"> { 
  
    
  { str }} <input type="text"> </li> </ul> <button @click="addFn">下标为1的位置新增一个</button>
export default { data(){ return { arr: ["老大", "新来的", "老二", "老三"] } }, methods: { addFn(){ this.arr.splice(1, 0, '新来的') } } }; 

讯享网 v-for先循环产生新的DOM结构, key是连续的, 和数据对应 然后比较新旧DOM结构, 找到区别, 打补丁到页面上 最后补一个li, 然后从第二个往后, 都要更新内容

(3)有key - 值为id

key的值只能是唯一不重复的, 字符串或数值 v-for不会移动DOM, 而是尝试复用, 就地更新,如果需要v-for移动DOM, 需要用特殊 attribute key 来提供一个排序提示 新DOM里数据的key存在, 去旧的虚拟DOM结构里找到key标记的标签, 复用标签 新DOM里数据的key存在, 去旧的虚拟DOM结构里没有找到key标签的标签, 创建 旧DOM结构的key, 在新的DOM结构里没有了, 则==移除key所在的标签
讯享网<template> <div> <ul> <li v-for="obj in arr" :key="obj.id"> { 
  
    
  { obj.name }} <input type="text"> </li> </ul> <button @click="btn">下标1位置插入新来的</button> </div> </template> <script> export default { data() { return { arr: [ { name: '老大', id: 50 }, { name: '老二', id: 31 }, { name: '老三', id: 10 } ], }; }, methods: { btn(){ this.arr.splice(1, 0, { id: 19, name: '新来的' }) } } }; </script> <style> </style>

先产生新旧虚拟DOM, 根据key比较

 总结: 不用key也不影响功能(就地更新), 添加key可以提高更新的性能

(4)有key和无key小结

 

17. vue基础_动态class

目标: 用v-bind给标签class设置动态的值

  • 语法:
    • :class="{类名: 布尔值}"

<template>  <div>    <!-- 语法:      :class="{类名: 布尔值}"      使用场景: vue变量控制标签是否应该有类名     -->    <p :class="{red_str: bool}">动态class</p>  </div> </template> ​ <script> export default {  data(){    return {      bool: true   } } } </script> ​ <style scoped>  .red_str{    color: red; } </style>

总结: 就是把类名保存在vue变量中赋予给标签

18. vue基础-动态style

目标: 给标签动态设置style的值

  • 语法
    • :style="{css属性: 值}"
讯享网<template>  <div>    <!-- 动态style语法      :style="{css属性名: 值}"     -->    <p :style="{backgroundColor: colorStr}">动态style</p>  </div> </template> ​ <script> export default {  data(){    return {      colorStr: 'red'   } } } </script> ​ <style> ​ </style>

总结: 动态style的key都是css属性名

案例-品牌管理

  • 需求1: 把默认数据显示到表格上
  • 需求2: 注意资产超过100的, 都用红色字体标记出来
  • 实现数据增删

细节:

① 先铺设静态页面 

② 此案例使用bootstrap, 需要下载, 并导入到工程main.js中

   bootstrap, 工程化开发, 模块化用npm/yarn下载引入使用

arn add bootstr

   在main.js - 引入bootstrap  

讯享网import "bootstrap/dist/css/bootstrap.css" // 默认找文件夹下的index文件(但是这个不是所以需要写路径)

③ 用v-for配合默认数据, 把数据默认铺设到表格上显示

④ 直接在标签上, 大于100价格, 动态设置red类名

<template> <div id="app"> <div class="container"> <!-- 顶部框模块 --> <div class="form-group"> <div class="input-group"> <h4>品牌管理</h4> </div> </div> <!-- 数据表格 --> <table class="table table-bordered table-hover mt-2"> <thead> <tr> <th>编号</th> <th>资产名称</th> <th>价格</th> <th>创建时间</th> <th>操作</th> </tr> </thead> <tbody> <tr v-for="obj in list" :key="obj.id"> <td>{ 
  
    
  { obj.id }}</td> <td>{ 
  
    
  { obj.name }}</td> <!-- 如果价格超过100,就有red这个类 --> <td :class="{red: obj.price > 100}">{ 
  
    
  { obj.price }}</td> <td>{ 
  
    
  { obj.time }}</td> <td><a href="#" >删除</a></td> </tr> </tbody> <!-- <tfoot > <tr> <td colspan="5" style="text-align: center">暂无数据</td> </tr> </tfoot> --> </table> <!-- 添加资产 --> <form class="form-inline"> <div class="form-group"> <div class="input-group"> <input type="text" class="form-control" placeholder="资产名称" v-model="name" /> </div> </div> &nbsp;&nbsp;&nbsp;&nbsp; <div class="form-group"> <div class="input-group"> <input type="text" class="form-control" placeholder="价格" v-model.number="price" /> </div> </div> &nbsp;&nbsp;&nbsp;&nbsp; <!-- 4. 阻止表单提交(刷新网页数据又回去了) --> <button class="btn btn-primary" @click.prevent="addFn">添加资产</button> </form> </div> </div> </template> <script> export default { data() { return { name: "", // 名称 price: 0, // 价格 list: [ { id: 100, name: "外套", price: 199, time: new Date('2010-08-12')}, { id: 101, name: "裤子", price: 34, time: new Date('2013-09-01') }, { id: 102, name: "鞋", price: 25.4, time: new Date('2018-11-22') }, { id: 103, name: "头发", price: 19900, time: new Date('2020-12-12') } ], }; }, methods: { addFn(){ // 5. 判断是否为空 if (this.name.trim().length === 0 || this.price === 0) { alert("不能为空") return } // 3. 把值以对象形式-插入list this.list.push({ // 当前数组最后一个对象的id+1作为新对象id值 id: this.list[this.list.length - 1].id + 1, name: this.name, price: this.price, time: new Date() }) } // ...其他代码 delFn(id){ // 通过id找到这条数据在数组中下标 let index = this.list.findIndex(obj => obj.id === id) this.list.splice(index, 1) } } }; </script> <style > .red{ color: red; } </style>

五、vue过滤器

1. vue过滤器-定义使用

目的: 转换格式, 过滤器就是一个函数, 传入值返回处理后的值

 例子:

  • 全局定义字母都大写的过滤器

  • 局部定义字符串翻转的过滤器

讯享网<template>  <div>    <p>原来的样子: { 
  
    
  { msg }}</p>    <!-- 2. 过滤器使用      语法: { 
  
    
  { 值 | 过滤器名字 }}     -->    <p>使用翻转过滤器: { 
  
    
  { msg | reverse }}</p>    <p :title="msg | toUp">鼠标长停</p>  </div> </template> ​ <script> export default {  data(){    return {      msg: 'Hello, Vue'   } },  // 方式2: 局部 - 过滤器  // 只能在当前vue文件内使用  /*     语法:     filters: {       过滤器名字 (val) {         return 处理后的值       }     } */  filters: {    toUp (val) {      return val.toUpperCase()   } } } </script> ​ <style> ​ </style>

总结: 把值转成另一种形式, 使用过滤器, Vue3用函数替代了过滤器.

全局注册最好在main.js中注册, 一处注册到处使用

2. vue过滤器-传参和多过滤器

目标: 可同时使用多个过滤器, 或者给过滤器传参

  • 语法:
    • 过滤器传参: vue变量 | 过滤器(实参)
    • 多个过滤器: vue变量 | 过滤器1 | 过滤器2

<template>  <div>    <p>原来的样子: { 
  
    
  { msg }}</p>    <!-- 1.      给过滤器传值      语法: vue变量 | 过滤器名(值)     -->    <p>使用翻转过滤器: { 
  
    
  { msg | reverse('|') }}</p>    <!-- 2.      多个过滤利使用      语法: vue变量 | 过滤器1 | 过滤器2     -->    <p :title="msg | toUp | reverse('|')">鼠标长停</p>  </div> </template> ​ <script> export default {  data(){    return {      msg: 'Hello, Vue'   } },  filters: {    toUp (val) {      return val.toUpperCase()   } } } </script> ​ <style> ​ </style>

总结: 过滤器可以传参, 还可以对某个过滤器结果, 后面在使用一个过滤器

3. 案例-品牌管理(时间格式化)

(1)下载moment处理日期的第三方工具模块

           moment官网文档: Moment.js 中文网 | 开发文档

讯享网yarn add moment

(2)定义过滤器, 把时间用moment模块格式化, 返回我们想要的格式

// 目标: 处理时间 // 1. 下载moment模块 import moment from 'moment' ​ ​ // 2. 定义过滤器, 编写内部代码 filters: {    formatDate (val){        return moment(val).format('YYYY-MM-DD')   } } ​ <!-- 3. 使用过滤器 --> <td>{ 
  
    
  { obj.time | formatDate }}</td>

六、vue计算属性

1. vue计算属性-computed

目标: 一个数据, 依赖另外一些数据计算而来的结果

语法:

  • 讯享网computed: {    "计算属性名" () {        return "值"   } }

需求:

  • 需求: 求2个数的和显示到页面上
<template>  <div>    <p>{ 
  
    
  { num }}</p>  </div> </template> ​ <script> export default {  data(){    return {      a: 10,      b: 20   } },  // 计算属性:  // 场景: 一个变量的值, 需要用另外变量计算而得来  /*   语法:   computed: {     计算属性名 () {       return 值     }   } */ // 注意: 计算属性和data属性都是变量-不能重名 // 注意2: 函数内变量变化, 会自动重新计算结果返回  computed: {    num(){      return this.a + this.b   } } } </script> ​ <style> ​ </style>

注意: 计算属性也是vue数据变量, 所以不要和data里重名, 用法和data相同

总结: 一个数据, 依赖另外一些数据计算而来的结果

2. vue计算属性-缓存

目标: 计算属性是基于它们的依赖项的值结果进行缓存的,只要依赖的变量不变, 都直接从缓存取结果

讯享网<template>  <div>    <p>{ 
  
    
  { reverseMessage }}</p>    <p>{ 
  
    
  { reverseMessage }}</p>    <p>{ 
  
    
  { reverseMessage }}</p>    <p>{ 
  
    
  { getMessage() }}</p>    <p>{ 
  
    
  { getMessage() }}</p>    <p>{ 
  
    
  { getMessage() }}</p>  </div> </template> ​ <script> export default {  data(){    return {      msg: "Hello, Vue"   } },  // 计算属性优势:  // 带缓存  // 计算属性对应函数执行后, 会把return值缓存起来  // 依赖项不变, 多次调用都是从缓存取值  // 依赖项值-变化, 函数会"自动"重新执行-并缓存新的值  computed: {    reverseMessage(){      console.log("计算属性执行了");      return this.msg.split("").reverse().join("")   } },  methods: {    getMessage(){      console.log("函数执行了");      return this.msg.split("").reverse().join("")   } } } </script> ​ <style> ​ </style>

总结: 计算属性根据依赖变量结果缓存, 依赖变化重新计算结果存入缓存, 比普通方法性能更高

3. vue计算属性-完整写法

目标: 计算属性也是变量, 如果想要直接赋值, 需要使用完整写法

语法:

computed: {    "属性名": {        set(值){                   },        get() {            return "值"       }   } }

需求:

  • 计算属性给v-model使用

页面准备输入框

讯享网<template>  <div>      <div>          <span>姓名:</span>          <input type="text" v-model="full">      </div>  </div> </template> ​ <script> // 问题: 给计算属性赋值 - 需要setter // 解决: /*   完整语法:   computed: {       "计算属性名" (){},       "计算属性名": {           set(值){ ​           },           get(){               return 值           }       }   } */ export default {    computed: {        full: {            // 给full赋值触发set方法            set(val){                console.log(val)           },            // 使用full的值触发get方法            get(){                return "无名氏"           }       }   } } </script> ​ <style> ​ </style>

总结: 想要给计算属性赋值, 需要使用set方法

4. 案例-品牌管理(总价和均价)

目标: 基于之前的案例, 完成总价和均价的计算效果

此处只修改了变化的代码

<tr style="background-color: #EEE">     <td>统计:</td>     <td colspan="2">总价钱为: { 
  
    
  { allPrice }}</td>     <td colspan="2">平均价: { 
  
    
  { svgPrice }}</td> </tr> ​ <script> // 目标: 总价和均价显示 // 1. 末尾补tr - 显示总价和均价 export default {  // ...源代码省略  // 2. 计算属性  computed: {      allPrice(){          // 3. 求总价          return this.list.reduce((sum, obj) => sum += obj.price, 0)     },      avgPrice(){          // 4. 求均价 - 保留2位小数          return (this.allPrice / this.list.length).toFixed(2)     } } } </script>

总结: 总价来源于所有数据计算而来的结果, 故采用计算属性

5. 案例-全选反选

讯享网<template> <div> <span>全选:</span> <!-- 4. v-model 关联全选 - 选中状态 --> <input type="checkbox" v-model="isAll"/> <button>反选</button> <ul> <li v-for="(obj, index) in arr" :key="index"> <!-- 3. 对象.c - 关联 选中状态 --> <input type="checkbox" v-model="obj.c"/> <span>{ 
  
    
  { obj.name }}</span> </li> </ul> </div> </template> <script> // 目标: 小选框 -> 全选 // 1. 标签+样式+js准备好 // 2. 把数据循环展示到页面上 export default { data() { return { arr: [ { name: "猪八戒", c: false, }, { name: "孙悟空", c: false, }, { name: "唐僧", c: false, }, { name: "白龙马", c: false, }, ], }; }, // 5. 计算属性-isAll computed: { isAll: { set(val){ // 7. 全选框 - 选中状态(true/false) this.arr.forEach(obj => obj.c = val) }, get(){ // 6. 统计小选框状态 -> 全选状态 // every口诀: 查找数组里"不符合"条件, 直接原地返回false return this.arr.every(obj => obj.c === true) } } } methods: { btn(){ // 8. 让数组里对象的c属性取反再赋予回去 this.arr.forEach(obj => obj.c = !obj.c) } } }; </script>

七、 vue侦听器

1. vue侦听器-watch

目标: 可以侦听data/computed属性值改变

语法:

watch: {    "被侦听的属性名" (newVal, oldVal){           } }

完整例子代码:

讯享网<template>  <div>    <input type="text" v-model="name">  </div> </template> ​ <script> export default {  data(){    return {      name: ""   } },  // 目标: 侦听到name值的改变  /* 语法:   watch: {     变量名 (newVal, oldVal){       // 变量名对应值改变这里自动触发     }   } */  watch: {    // newVal: 当前最新值    // oldVal: 上一刻值    name(newVal, oldVal){      console.log(newVal, oldVal);   } } } </script> ​ <style> ​ </style>

总结: 想要侦听一个属性变化, 可使用侦听属性watch

2. vue侦听器-深度侦听和立即执行

目标: 侦听复杂类型, 或者立即执行侦听函数

  • 语法:
    watch: {    "要侦听的属性名": {        immediate: true, // 立即执行        deep: true, // 深度侦听复杂类型内变化        handler (newVal, oldVal) {                   }   } }

完整例子代码:

讯享网<template>  <div>    <input type="text" v-model="user.name">    <input type="text" v-model="user.age">  </div> </template> ​ <script> export default {  data(){    return {      user: {        name: "",        age: 0     }   } },  // 目标: 侦听对象  /* 语法:   watch: {     变量名 (newVal, oldVal){       // 变量名对应值改变这里自动触发     },     变量名: {       handler(newVal, oldVal){ ​       },       deep: true, // 深度侦听(对象里面层的值改变)       immediate: true // 立即侦听(网页打开handler执行一次)     }   } */  watch: {    user: {      handler(newVal, oldVal){        // user里的对象        console.log(newVal, oldVal);     },      deep: true,      immediate: true   } } } </script> ​ <style> ​ </style>

总结: immediate立即侦听, deep深度侦听, handler固定方法触发

3. 案例-品牌管理(数据缓存)

目标: 侦听list变化, 同步到浏览器本地

  • 需求: 把品牌管理的数据实时同步到本地缓存

分析:

① 在watch侦听list变化的时候, 把最新的数组list转成JSON字符串存入到localStorage本地

② data里默认把list变量从本地取值, 如果取不到给个默认的空数组

效果:

新增/删除 – 刷新页面 – 数据还在

<script> import moment from "moment"; export default {  data() {    return {      name: "", // 名称      price: 0, // 价格      // 3. 本地取出缓存list      list: JSON.parse(localStorage.getItem('pList')) || [],   }; },  // ...其他代码省略  watch: {    list: {      handler(){        // 2. 存入本地        localStorage.setItem('pList', JSON.stringify(this.list))     },      deep: true   } } }; </script>

八、vue组件

1. vue组件_概念

组件是可复用的 Vue 实例, 封装标签, 样式和JS代码

组件化 :封装的思想,把页面上 可重用的部分 封装为 组件,从而方便项目的 开发 和 维护

一个页面, 可以拆分成一个个组件,一个组件就是一个整体, 每个组件可以有自己独立的 结构 样式 和 行为(html, css和js)

2. vue组件_基础使用

讯享网口诀: 哪部分标签复用, 就把哪部分封装到组件内 (重要): 组件内template只能有一个根标签 (重要): 组件内data必须是一个函数, 独立作用域

步骤:

(1)创建组件 components/Pannel.vue

封装标签+样式+js - 组件都是独立的, 为了复用

<template> <div> <div class="title"> <h4>芙蓉楼送辛渐</h4> <span class="btn" @click="isShow = !isShow"> { 
  
    
  { isShow ? "收起" : "展开" }} </span> </div> <div class="container" v-show="isShow"> <p>寒雨连江夜入吴,</p> <p>平明送客楚山孤。</p> <p>洛阳亲友如相问,</p> <p>一片冰心在玉壶。</p> </div> </div> </template> <script> export default { data() { return { isShow: false, }; }, }; </script> <style scoped> .title { display: flex; justify-content: space-between; align-items: center; border: 1px solid #ccc; padding: 0 1em; } .title h4 { line-height: 2; margin: 0; } .container { border: 1px solid #ccc; padding: 0 1em; } .btn { /* 鼠标改成手的形状 */ cursor: pointer; } </style>

 (2)注册组件: 创建后需要注册后再使用

全局 - 注册使用

全局入口在main.js, 在new Vue之上注册

语法:

讯享网import Vue from 'vue' import 组件对象 from 'vue文件路径' Vue.component("组件名", 组件对象)

main.js

// 目标: 全局注册 (一处定义到处使用) // 1. 创建组件 - 文件名.vue // 2. 引入组件 import Pannel from './components/Pannel' // 3. 全局 - 注册组件 /* 语法: Vue.component("组件名", 组件对象) */ Vue.component("PannelG", Pannel)

全局注册PannelG组件名后, 就可以当做标签在任意Vue文件中template里用

单双标签都可以或者小写加-形式, 运行后, 会把这个自定义标签当做组件解析, 使用组件里封装的标签替换到这个位置

讯享网<PannelG></PannelG> <PannelG/> <pannel-g></pannel-g>
局部 - 注册使用

语法:

import 组件对象 from 'vue文件路径' ​ export default {    components: {        "组件名": 组件对象   } }

任意vue文件中中引入, 注册, 使用

讯享网<template>  <div id="app">    <h3>案例:折叠面板</h3>    <!-- 4. 组件名当做标签使用 -->    <!-- <组件名></组件名> -->    <PannelG></PannelG>    <PannelL></PannelL>  </div> </template> ​ <script> // 目标: 局部注册 (用的多) // 1. 创建组件 - 文件名.vue // 2. 引入组件 import Pannel from './components/Pannel_1' export default {  // 3. 局部 - 注册组件  /*   语法:   components: {     "组件名": 组件对象   } */  components: {    PannelL: Pannel } } </script>

(3)组件使用总结

(1)(创建)封装html+css+vue到独立的.vue文件中

(2)(引入注册)组件文件 => 得到组件配置对象

(3)(使用)当前页面当做标签使用

4. vue组件-scoped作用

目的: 解决多个组件样式名相同, 冲突问题

需求: div标签名选择器, 设置背景色

问题: 发现组件里的div和外面的div都生效了

解决: 给Pannel.vue组件里style标签上加scoped属性即可

讯享网<style scoped>

在style上加入scoped属性, 就会在此组件的标签上加上一个随机生成的data-v开头的属性

而且必须是当前组件的元素, 才会有这个自定义属性, 才会被这个样式作用到

 总结: style上加scoped, 组件内的样式只在当前vue组件生效

九、vue组件通信

因为每个组件的变量和值都是独立的 组件通信先暂时关注父传子, 子传父 父: 使用其他组件的vue文件 子: 被引入的组件(嵌入) 例如: App.vue(父) MyProduct.vue(子)

1. vue组件通信_父向子-props

目的: 从外面给组件内传值

(1)父组件 -> 子组件 传值

讯享网步骤: (1)创建组件components(父)/MyProduct.vue (子) (2)子组件内在props定义变量, 用于接收外部传入的值 (3)父组件中引入注册组件, 使用时, 传入具体数据给组件显示

components/MyProduct.vue - 准备标签(子组件内, 定义变量, 准备接收, 然后使用变量)

<template>  <div class="my-product">    <h3>标题: { 
  
    
  { title }}</h3>    <p>价格: { 
  
    
  { price }}元</p>    <p>{ 
  
    
  { intro }}</p>  </div> </template> ​ <script> export default {  props: ['title', 'price', 'intro'] } </script> ​ <style> .my-product {  width: 400px;  padding: 20px;  border: 2px solid #000;  border-radius: 5px;  margin: 10px; } </style>

App.vue中使用并传入数据(引入组件, 注册组件, 使用组件, 传值进去)

讯享网<template>  <div>    <!--      目标: 父(App.vue) -> 子(MyProduct.vue) 分别传值进入      需求: 每次组件显示不同的数据信息      步骤(口诀):        1. 子组件 - props - 变量 (准备接收)        2. 父组件 - 传值进去     -->    <Product title="好吃的口水鸡" price="50" intro="开业大酬宾, 全场8折"></Product>    <Product title="好可爱的可爱多" price="20" intro="老板不在家, 全场1折"></Product>    <Product title="好贵的北京烤鸭" price="290" :intro="str"></Product>  </div> </template> ​ <script> // 1. 创建组件 (.vue文件) // 2. 引入组件 import Product from './components/MyProduct' export default {  data(){    return {      str: "好贵啊, 快来啊, 好吃"   } },  // 3. 注册组件  components: {    // Product: Product // key和value变量名同名 - 简写    Product } } </script> ​ <style> ​ </style>

总结: 组件封装复用的标签和样式, 而具体数据要靠外面传入

(2)vue组件通信_父向子-配合循环

目的: 把数据循环分别传入给组件内显示

数据

list: [   { id: 1, proname: "超级好吃的棒棒糖", proprice: 18.8, info: '开业大酬宾, 全场8折' },   { id: 2, proname: "超级好吃的大鸡腿", proprice: 34.2, info: '好吃不腻, 快来买啊' },   { id: 3, proname: "超级无敌的冰激凌", proprice: 14.2, info: '炎热的夏天, 来个冰激凌了' }, ],
讯享网<template>  <div>    <MyProduct v-for="obj in list" :key="obj.id"    :title="obj.proname"    :price="obj.proprice"    :intro="obj.info"    ></MyProduct>  </div> </template> ​ <script> // 目标: 循环使用组件-分别传入数据 // 1. 创建组件 // 2. 引入组件 import MyProduct from './components/MyProduct' export default {  data() {    return {      list: [       {          id: 1,          proname: "超级好吃的棒棒糖",          proprice: 18.8,          info: "开业大酬宾, 全场8折",       },       {          id: 2,          proname: "超级好吃的大鸡腿",          proprice: 34.2,          info: "好吃不腻, 快来买啊",       },       {          id: 3,          proname: "超级无敌的冰激凌",          proprice: 14.2,          info: "炎热的夏天, 来个冰激凌了",       },     ],   }; },  // 3. 注册组件  components: {    // MyProduct: MyProduct    MyProduct } }; </script> ​ <style> </style>

2. 单向数据流

从父到子的数据流向,叫单向数据流

在vue中需要遵循单向数据流原则

父组件的数据发生了改变,子组件会自动跟着变 子组件不能直接修改父组件传递过来的props, props变量本身是只读不能重新赋值 注意: 父组件传给子组件的是一个对象,子组件修改对象的属性,是不会报错的, 对象是引用类型, 互相更新

原因: 子组件修改, 不通知父级, 造成数据不一致性

        第一个MyProduct.vue内自己修改商品价格为5.5, 但是App.vue里原来还记着18.8 - 数据 不一致了

总结: props变量本身是不能重新赋值的

3. vue组件通信_子向父

目标: 从子组件把值传出来给外面使用

语法:

  • 父: @自定义事件名="父methods函数"
  • 子: this.$emit("自定义事件名", 传值) - 执行父methods里函数代码

  

(1)父组件内, 绑定自定义事件和事件处理函数
          @自定义事件名="父methods里函数名"

  App.vue

讯享网<template> <div> <!-- 目标: 子传父 --> <!-- 1. 父组件, @自定义事件名="父methods函数" --> <MyProduct v-for="(obj, ind) in list" :key="obj.id" :title="obj.proname" :price="obj.proprice" :intro="obj.info" :index="ind" @subprice="fn" ></MyProduct> </div> </template> <script> import MyProduct from './components/MyProduct_sub' export default { data() { return { list: [ { id: 1, proname: "超级好吃的棒棒糖", proprice: 18.8, info: "开业大酬宾, 全场8折", }, { id: 2, proname: "超级好吃的大鸡腿", proprice: 34.2, info: "好吃不腻, 快来买啊", }, { id: 3, proname: "超级无敌的冰激凌", proprice: 14.2, info: "炎热的夏天, 来个冰激凌了", }, ], }; }, components: { MyProduct }, methods: { fn(inde, price){ // 逻辑代码 this.list[inde].proprice > 1 && (this.list[inde].proprice = (this.list[inde].proprice - price).toFixed(2)) } } }; </script> <style> </style>

(2)子组件内, 恰当的时机, 触发父给我绑的自定义事件, 导致父methods里事件处理函数执行

 components/MyProduct_sub.vue

<template> <div class="my-product"> <h3>标题: { 
  
    
  { title }}</h3> <p>价格: { 
  
    
  { price }}元</p> <p>{ 
  
    
  { intro }}</p> <button @click="subFn">宝刀-砍1元</button> </div> </template> <script> import eventBus from '../EventBus' export default { props: ['index', 'title', 'price', 'intro'], methods: { subFn(){ this.$emit('subprice', this.index, 1) // 子向父 eventBus.$emit("send", this.index, 1) // 跨组件 } } } </script> <style> .my-product { width: 400px; padding: 20px; border: 2px solid #000; border-radius: 5px; margin: 10px; } </style>

总结: 父自定义事件和方法, 等待子组件触发事件给方法传值

3. vue组件通信-EventBus

目标: 常用于跨组件通信时使用

两个没有任何引入关系的组件, 要如何互相通信呢?

两个组件的关系非常的复杂,通过父子组件通讯是非常麻烦的。

这时候可以使用通用的组件通讯方案:事件总线(event-bus)

  

       总结: 空的Vue对象, 只负责$on注册事件, $emit触发事件, 一定要确保$on先执行

十、vue的生命周期

一组件从 创建 到 销毁 的整个过程就是生命周期

Vue_生命周期

1. 钩子函数

目标: Vue 框架内置函数,随着组件的生命周期阶段,自动执行

作用: 特定的时间点,执行特定的操作

场景: 组件创建完毕后,可以在created 生命周期函数中发起Ajax 请求,从而初始化 data 数据

分类: 4大阶段8个方法

  • 初始化
  • 挂载
  • 更新
  • 销毁
阶段 方法名 方法名
初始化 beforeCreate created
挂载 beforeMount mounted
更新 beforeUpdate updated
销毁 beforeDestroy destroyed

官网文档https://cn.vuejs.org/v2/guide/instance.html#%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E5%9B%BE%E7%A4%BA

下图展示了实例的生命周期。你不需要立马弄明白所有的东西,不过随着你的不断学习和使用,它的参考价值会越来越高。

2. 初始化阶段

目标: 掌握初始化阶段2个钩子函数作用和执行时机

讯享网含义讲解: 1.new Vue() – Vue实例化(组件也是一个小的Vue实例) 2.Init Events & Lifecycle – 初始化事件和生命周期函数 3.beforeCreate – 生命周期钩子函数被执行 4.Init injections&reactivity – Vue内部添加data和methods等 5.created – 生命周期钩子函数被执行, 实例创建 6.接下来是编译模板阶段 –开始分析 7.Has el option? – 是否有el选项 – 检查要挂到哪里 ​ 没有. 调用$mount()方法 ​ 有, 继续检查template选项

 

 components/Life.vue - 创建一个文件

 <script> export default { data(){ return { msg: "hello, Vue" } }, // 一. 初始化 // new Vue()以后, vue内部给实例对象添加了一些属性和方法, data和methods初始化"之前" beforeCreate(){ console.log("beforeCreate -- 执行"); console.log(this.msg); // undefined }, // data和methods初始化以后 // 场景: 网络请求, 注册全局事件 created(){ console.log("created -- 执行"); console.log(this.msg); // hello, Vue this.timer = setInterval(() => { console.log("哈哈哈"); }, 1000) } } </script>

App.vue - 引入使用

讯享网<template> <div> <h1>1. 生命周期</h1> <Life></Life> </div> </template> <script> import Life from './components/Life' export default { components: { Life } } </script>

3. 挂载阶段

目标: 掌握挂载阶段2个钩子函数作用和执行时机

含义讲解: 1.template选项检查 有 - 编译template返回render渲染函数 无 – 编译el选项对应标签作为template(要渲染的模板) 2.虚拟DOM挂载成真实DOM之前 3.beforeMount – 生命周期钩子函数被执行 4.Create … – 把虚拟DOM和渲染的数据一并挂到真实DOM上 5.真实DOM挂载完毕 6.mounted – 生命周期钩子函数被执行 ​​​​​​​

讯享网<template> <div> <p>学习生命周期 - 看控制台打印</p> <p id="myP">{ 
  
    
  { msg }}</p> </div> </template> <script> export default { // ...省略其他代码 // 二. 挂载 // 真实DOM挂载之前 // 场景: 预处理data, 不会触发updated钩子函数 beforeMount(){ console.log("beforeMount -- 执行"); console.log(document.getElementById("myP")); // null this.msg = "重新值" }, // 真实DOM挂载以后 // 场景: 挂载后真实DOM mounted(){ console.log("mounted -- 执行"); console.log(document.getElementById("myP")); // p } } </script>

4. 更新阶段

目标: 掌握更新阶段2个钩子函数作用和执行时机

含义讲解: 1.当data里数据改变, 更新DOM之前 2.beforeUpdate – 生命周期钩子函数被执行 3.Virtual DOM…… – 虚拟DOM重新渲染, 打补丁到真实DOM 4.updated – 生命周期钩子函数被执行 5.当有data数据改变 – 重复这个循环

​​​​​​​

components/Life.vue - 创建一个文件

准备ul+li循环, 按钮添加元素, 触发data改变->导致更新周期开始

讯享网<template> <div> <p>学习生命周期 - 看控制台打印</p> <p id="myP">{ 
  
    
  { msg }}</p> <ul id="myUL"> <li v-for="(val, index) in arr" :key="index"> { 
  
    
  { val }} </li> </ul> <button @click="arr.push(1000)">点击末尾加值</button> </div> </template> <script> export default { data(){ return { msg: "hello, Vue", arr: [5, 8, 2, 1] } }, // ...省略其他代码 // 三. 更新 // 前提: data数据改变才执行 // 更新之前 beforeUpdate(){ console.log("beforeUpdate -- 执行"); console.log(document.querySelectorAll("#myUL>li")[4]); // undefined }, // 更新之后 // 场景: 获取更新后的真实DOM updated(){ console.log("updated -- 执行"); console.log(document.querySelectorAll("#myUL>li")[4]); // li } } </script>

5. 销毁阶段

目标: 掌握销毁阶段2个钩子函数作用和执行时机

含义讲解: 1.当$destroy()被调用 – 比如组件DOM被移除(例v-if) 2.beforeDestroy – 生命周期钩子函数被执行 3.拆卸数据监视器、子组件和事件侦听器 4.实例销毁后, 最后触发一个钩子函数 5.destroyed – 生命周期钩子函数被执行

 components/Life.vue - 准备生命周期方法(Life组件即将要被删除)

讯享网<script> export default { // ...省略其他代码 // 四. 销毁 // 前提: v-if="false" 销毁Vue实例 // 场景: 移除全局事件, 移除当前组件, 计时器, 定时器, eventBus移除事件$off方法 beforeDestroy(){ // console.log('beforeDestroy -- 执行'); clearInterval(this.timer) }, destroyed(){ // console.log("destroyed -- 执行"); } } </script>

主要: App.vue - 点击按钮让Life组件从DOM上移除 -> 导致Life组件进入销毁阶段

<Life v-if="show"></Life> <button @click="show = false">销毁组件</button> <script> data(){ return { show: true } }, </script>

十一、$refs和$nextTick

1.  $refs-获取DOM

目标: 利用 ref 和 $refs 可以用于获取 dom 元素

components/More.vue

讯享网<template> <div> <p>1. 获取原生DOM元素</p> <h1 id="h" ref="myH">我是一个孤独可怜又能吃的h1</h1> </div> </template> <script> // 目标: 获取组件对象 // 1. 创建组件/引入组件/注册组件/使用组件 // 2. 组件起别名ref // 3. 恰当时机, 获取组件对象 export default { mounted(){ console.log(document.getElementById("h")); // h1 console.log(this.$refs.myH); // h1 } } </script> <style> </style>

总结: 通过id / ref, 都可以获取原生DOM标签

2. $refs-获取组件对象

目标: 获取组件对象, 调用组件里方法

components/Child/Demo.vue

<template>  <div>      <p>我是Demo组件</p>  </div> </template> ​ <script> export default {    methods: {        fn(){            console.log("demo组件内的方法被调用了");       }   } } </script>

More.vue - 获取组件对象 - 调用组件方法

讯享网<template>  <div>      <p>1. 获取原生DOM元素</p>      <h1 id="h" ref="myH">我是一个孤独可怜又能吃的h1</h1>      <p>2. 获取组件对象 - 可调用组件内一切</p>      <Demo ref="de"></Demo>  </div> </template> ​ <script> // 目标: 获取组件对象 // 1. 创建组件/引入组件/注册组件/使用组件 // 2. 组件起别名ref // 3. 恰当时机, 获取组件对象 import Demo from './Child/Demo' export default {    mounted(){        console.log(document.getElementById("h")); // h1        console.log(this.$refs.myH); // h1 ​        let demoObj = this.$refs.de;        demoObj.fn()   },    components: {        Demo   } } </script>

总结: ref定义值, 通过$refs.值 来获取组件对象, 就能继续调用组件内的变量

 3.  $nextTick使用

Vue更新DOM-异步的

目标: 点击count++, 马上通过"原生DOM"拿标签内容, 无法拿到新值

components/Move.vue - 继续新增第三套代码

<template>  <div>      <p>1. 获取原生DOM元素</p>      <h1 id="h" ref="myH">我是一个孤独可怜又能吃的h1</h1>      <p>2. 获取组件对象 - 可调用组件内一切</p>      <Demo ref="de"></Demo>      <p>3. vue更新DOM是异步的</p>      <p ref="myP">{ 
  
    
  { count }}</p>      <button @click="btn">点击count+1, 马上提取p标签内容</button>  </div> </template> ​ <script> // 目标: 获取组件对象 // 1. 创建组件/引入组件/注册组件/使用组件 // 2. 组件起别名ref // 3. 恰当时机, 获取组件对象 import Demo from './Child/Demo' export default {    mounted(){        console.log(document.getElementById("h")); // h1        console.log(this.$refs.myH); // h1 ​        let demoObj = this.$refs.de;        demoObj.fn()   },    components: {        Demo   },    data(){        return {            count: 0       }   },    methods: {        btn(){            this.count++; // vue监测数据更新, 开启一个DOM更新队列(异步任务)            console.log(this.$refs.myP.innerHTML); // 0 ​            // 原因: Vue更新DOM异步            // 解决: this.$nextTick()            // 过程: DOM更新完会挨个触发$nextTick里的函数体             this.$nextTick(() => {                console.log(this.$refs.myP.innerHTML); // 1           })       }   } } </script>

总结: 因为DOM更新是异步的

4.  $nextTick使用场景

目标: 点击搜索按钮, 弹出聚焦的输入框, 按钮消失

components/Tick.vue

讯享网<template>  <div>      <input ref="myInp" type="text" placeholder="这是一个输入框" v-if="isShow">      <button v-else @click="btn">点击我进行搜索</button>  </div> </template> ​ <script> // 目标: 点按钮(消失) - 输入框出现并聚焦 // 1. 获取到输入框 // 2. 输入框调用事件方法focus()达到聚焦行为 export default {    data(){        return {            isShow: false       }   },    methods: {        async btn(){            this.isShow = true;            // this.$refs.myInp.focus()            // 原因: data变化更新DOM是异步的            // 输入框还没有挂载到真实DOM上            // 解决:            // this.$nextTick(() => {            //     this.$refs.myInp.focus()            // })            // 扩展: await取代回调函数            // $nextTick()原地返回Promise对象            await this.$nextTick()            this.$refs.myInp.focus()       }   } } </script>

5. 组件name属性使用

目标: 可以用组件的name属性值, 来注册组件名字

问题: 组件名不是可以随便写的?

答案: 我们封装的组件-可以自己定义name属性组件名-让使用者有个统一的前缀风格

components/Com.vue

<template>  <div>      <p>我是一个Com组件</p>  </div> </template> ​ <script> export default {    name: "ComNameHaHa" // 注册时可以定义自己的名字 } </script>

App.vue - 注册和使用

讯享网<template>  <div>    <h1>1. 生命周期</h1>    <Life v-if="show"></Life>    <button @click="show = false">销毁组件</button>    <hr>    <h1>2. axios使用</h1>    <UseAxios></UseAxios>    <hr>    <h1>3. $refs的使用</h1>    <More></More>    <hr>    <h1>4. $nextTick使用场景</h1>    <Tick></Tick>    <hr>    <h1>5. 组件对象里name属性</h1>    <ComNameHaHa></ComNameHaHa>  </div> </template> ​ <script> import Life from './components/Life' import UseAxios from './components/UseAxios' import More from './components/More' import Tick from './components/Tick' import Com from './components/Com' export default {  data(){    return {      show: true   } },  components: {    Life,    UseAxios,    More,    Tick,   [Com.name]: Com // 对象里的key是变量的话[]属性名表达式    // "ComNameHaHa": Com } } </script>

小讯
上一篇 2025-02-22 11:54
下一篇 2025-04-07 17:46

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/41449.html