Vue2+Vue3

文章目录

  • 第 1 章:Vue 核心
    • 1、 Vue 简介
      • 1.官网
      • 2.介绍与描述
      • 3. Vue 的特点
      • 4. 与其它 JS 框架的关联
      • 5. Vue 周边库
    • 2、初始Vue
    • 3、模板语法
      • 1、Vue模板语法有2大类:
      • 2、插值语法和指令语法
    • 4、数据绑定
      • 1. 单向数据绑定
      • 2. 双向数据绑定
    • 5、el与data的两种写法
      • 1.e1有2种写法
      • 2.data有2种写法
      • 3.一个重要的原则:
    • 6、 MVVM 模型
    • 7、数据代理
      • 1、 Object.defineProperty方法:
      • 2、数据代理
        • 1、概念
        • 2、Vue中的数据代理:
        • 3、Vue中数据代理的好处:
        • 4、基本原理:
    • 8、事件处理
      • 1、事件的基本使用:
      • 2、事件修饰符
      • 3、键盘事件
    • 9、计算属性与监视
      • 1、计算属性 computed
      • 2、监视属性
        • 1、监视属性 watch及简写方式:
          • 1、深度监听 deep
      • 3、计算属性computed和监听属性watch的对比
    • 10、class 与style的绑定
    • 11、条件渲染
      • 1.V-if
      • 2.V-show
      • 3.备注:
    • 12、列表渲染
      • v-for指令
        • 1、遍历列表时key的作用(index作为key)
        • 2、遍历列表时key的作用(id作为key)
        • 3、面试题: react、vue中的key有什么作用? (key的内部原理)
    • 13、列表过滤
    • 14、列表排序
    • 15、vue检测数据的原理
    • 16、收集表单数据
    • 17、过滤器
    • 18、内置指令与自定义指令
      • 1、常用内置指令
    • 19、自定义指令
      • 1、自定义指令总结:
      • 2、自定义指令案例(函数式及对象式)
    • 20、生命周期
      • 1、vm的生命周期
      • 2、常用的生命周期钩子:
      • 3、关于销毁Vue实例
      • 常用指令总结
  • 第 2 章:Vue 组件化编程
    • 1、 模块与组件、模块化与组件化
      • 1. 模块
      • 2. 组件
      • 3. 模块化
      • 4. 组件化
    • 2、非单文件组件
      • 1、概念
    • 3、Vue中使用组件的三大步骤:
      • 一、 如何定义一个组件?
      • 二、如何注册组件?
      • 三、编写组件标签:
    • 4、组件的几个注意事项
      • 1.关于组件名:
      • 2.关于组件标签:
      • 3.一个简写方式:
    • 5、组件的嵌套
    • 6、关丁VueComponent:
    • 7、一个重要的内置关系:
      • 1.一个重要的内置关系:
      • 2.为什么要有这个关系:
    • 8、单文件组件
      • 1. 一个.vue 文件的组成(3 个部分)
  • 第 3 章:使用 Vue 脚手架
    • 1 初始化脚手架
      • 1、说明
      • 2 、具体步骤
        • 第一步(仅第一次执行):全局安装@vue/cli。
        • 第二步:切换到你要创建项目的目录,然后使用命令创建项目
        • 第三步:启动项目
        • 备注:
      • 3 模板项目的结构
      • 4、关于不同版本的Vue:
    • 2 ref 与 props
      • 1、ref
      • 2、props
    • 3 混入minxin
    • 4 插件
      • 1、功能:
      • 2、本质:
      • 3、定义插件:
      • 4、再main.js中使用插件:
    • 5、scoped样式
    • 6、组件的自定义事件![在这里插入图片描述](https://img-blog.csdnimg.cn/c6a95b336c154d3d9d524a4876250754.png)
    • 7、全局事件总线 (GlobalEventBus):任意组件间通信
    • 8、消息订阅与发布
      • 1 理解
      • 2 使用 PubSubJS
      • 3、总结:
      • nextTick
    • 9 、Vue封装的过度与动画
  • 第 4 章:Vue 中的 ajax
    • 1 解决开发环境 Ajax 跨域问题(代理服务器)
    • 2、vue脚手架配置代理
    • 3、插槽
      • 1.作用:
      • 2.分类:
      • 3.使用方式:
        • 1、默认插槽
        • 2、具名插槽
      • 3.作用域插槽:
        • 1.理解:
        • 2.具体编码
  • 第 5 章:vuex
    • 1、理解 vuex
      • 1 vuex 是什么
    • 2、什么时候使用 Vuex
    • 3、搭建环境
      • 1、创建文件:src/store/index.js
      • 2、在mian.js中创建vm时传入store配置项
    • 4、基本使用
      • 1、初始化数据、配置actions、配置mutations,操作文件store.js
      • 2、组件中读取vuex中的数据
      • 3.组件中修改vuex中的数据:
    • 5、getters的使用
      • 1.概念:
      • 2.在store.js 中追加getters 配置
      • 3.组件中读取数据:
    • 6、四个map方法的使用
      • 1.mapstate方法:
      • 2.mapGetters方法:
      • 3.mapActions方法:
      • 4.mapMutations方法:
    • 7.模块化+命名空间
      • 1.目的:
      • 2.修改store.js
  • 第 6 章:vue-router路由
    • 1 vue-router 的理解
    • 2 对 SPA 应用的理解
    • 3 路由的理解
      • 1. 什么是路由?
      • 2. 路由分类
        • 1. 后端路由:
        • 2. 前端路由:
    • 4 路由的基本使用
      • 1.安装vue-router,命令:
      • 2.应用插件:
      • 3.编写router配置项:
      • 4.实现切换(active-class可配置高亮样式)
      • 5.指定展示位置
      • 6.几个注意点
    • 5、嵌套(多级)路由
      • 1.配置路由规则,使用children配置项:
      • 2.跳转 (要写完整路径) :
    • 6、路由的query传参
      • 1、传递参数
      • 2、接收参数
    • 7、命名路
      • 1.作用:
      • 2.如何使用
        • 1.给路由命名
        • 2.简化跳转:
    • 8、路由的params参数
      • 1、配置路由,声明接收 params参数
      • 2、传递参数
      • 3、接收参数
    • 9.路由的props配置
    • 10.```<router-link>```的replace属性
    • 11.编程式路由导航
    • 11、缓存路由组件
    • 12.两个新的生命周期钩子
    • 13、路由守卫
      • 1. 作用:
      • 2. 分类:
      • 3. 全局守卫:
      • 4. 独享守卫:
      • 5. 组件内守卫:
    • 14、路由器的两种工作模式
  • vue3快速上手
  • 一、创建Vue3.0工程
    • 1.使用 vue-cli 创建
    • 2.使用 vite 创建
    • 3.分析工程结构
  • 二、常用 Composition API
    • 1.拉开序幕的setup
    • 2.ref函数
    • 3.reactive函数
    • 4.Vue3.0中的响应式原理
      • vue2.x的响应式
      • Vue3.0的响应式
    • 5.reactive对比ref
    • 6.setup的两个注意点
    • 7.计算属性与监视
      • 1.computed函数
      • 2.watch函数
      • 3.watchEffect函数
    • 8.生命周期
    • 9.自定义hook函数
    • 10.toRef
  • 三、其它 Composition API
    • 1.shallowReactive 与 shallowRef
    • 2.readonly 与 shallowReadonly
    • 3.toRaw 与 markRaw
    • 4.customRef
    • 5.provide 与 inject
    • 6.响应式数据的判断
  • 四、Composition API 的优势
    • 1.Options API 存在的问题
    • .Composition API 的优势
  • 五、新的组件
    • 1.Fragment
    • 2.Teleport
    • 3.Suspense
  • 六、其他
    • 1.全局API的转移
    • 2.其他改变
  • 第三方库
  • 浏览器本地存储


第 1 章:Vue 核心

1、 Vue 简介

1.官网

  1. 英文官网: https://vuejs.org/
  2. 中文官网: https://cn.vuejs.org/

2.介绍与描述

1、动态构建用户界面的渐进式 JavaScript 框架

构造用户界面:将数据展示到用户可以看见的界面
渐进式:vue可以自底向上逐层的应用
简单应用:只需一个轻量小巧的核心库
复杂应用:可以引入各式各样的vue插件

2、 作者: 尤雨溪

3. Vue 的特点

  1. 遵循 MVVM 模式
  2. 编码简洁, 体积小, 运行效率高, 适合移动/PC 端开发
  3. 它本身只关注 UI, 也可以引入其它第三方库开发项目
  4. 采用组件化模式,提高代码复用率、且让代码更好维护
  5. 声明式编码,让编码人员无需直接操作DOM,提高开发效率

4. 与其它 JS 框架的关联

  1. 借鉴 Angular 的模板和数据绑定技术
  2. 借鉴 React 的组件化和虚拟 DOM 技术

5. Vue 周边库

  1. vue-cli: vue 脚手架
  2. vue-resource
  3. axios
  4. vue-router: 路由
  5. vuex: 状态管理
  6. element-ui: 基于 vue 的 UI 组件库(PC 端)等

2、初始Vue

1.想让Vue工作,就必须创建一个Vue实例,且要传入一个配置对象;
2.root容器里的代码依然符合htm1规范,只不过混入了一些特殊的Vue语法;
3.root容器里的代码被称为[Vue模板]:
4.Vue实例和容器是一 一对应的;
5.真实开发中只有一个Vue实例,并且会配合着组件一起使用;
6.{{xxx}}中的xxx要写js表达式,且xxx可以自动读取到data中的所有属性;
7.一旦data中的数据发生改变,那么模板中用到该数据的地方也会自动更新:
注意区分:JS表达式和JS代码
1、表达式:一个表达式会产成一个值,可以放在任何一个需要值的地方,如:a+b 等
2、JS代码:如for循环,if语句

<!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>
    <script type="text/javascript" src="../js/vue.js"></script>

</head>
<body>
    <div id="root">
        <h1>插值语法</h1>
        <h1>你好,我是:{{name}}</h1>
    </div>

    <script type="text/javascript">
        //阻止vue 在启动时生成生产提示
        Vue.config.productionTip=false

        //创建Vue实例
        new Vue({
            el:'#root',//el用于指定当前vue实例为那个容器服务,值通常为css选择器字符串
            data:{ //data中用来存储数据,数据供el所指定的容器去使用,值我们暂时先写成一个对象
                name:'vue'
            }
        })
    </script>
</body>
</html>

3、模板语法

1、Vue模板语法有2大类:

1.插值语法:
功能:用于解析标签体内容。(双标签中间的内容)
写法: {{xxx}},xxx是js表达式,且可以直接读取到data中的所有属性。
2.指令语法:
功能:用于解析标签(包括: 标签属性、标签体内容、绑定事件…)
举例:v-bind:href="xxx”或 简写为 :href=“xxx”,xxx同样要写js表达式,且可以直接读取到data中的所有属性。
备注:Vue中有很多的指令,且形式都是: v-???,此处我们只是拿v-bind举个例子。

2、插值语法和指令语法

<body>
    <div id="root">
        <h1>插值语法</h1>
        <h2>Hello,{{name}}</h2>
        <h1>指令语法</h1>
        <!-- 原始写法 -->
        <a v-bind:href="school.url">点击我去{{school.name}}</a>
        <a :href="school.url"></a>
    </div>

    <script type="text/javascript">
        //阻止vue 在启动时生成生产提示
        Vue.config.productionTip=false

        //创建Vue实例
        new Vue({
            el:'#root',//el用于指定当前vue实例为那个容器服务,值通常为css选择器字符串
            data:{ //data中用来存储数据,数据供el所指定的容器去使用,值我们暂时先写成一个对象
                name:'vue',
                school:{
                    name:'学习',
                    url:'https:www.baidu.com'
                }
            }
        })
    </script>
</body>

4、数据绑定

1. 单向数据绑定

  1. 语法:v-bind:href =“xxx” 或简写为 :href
  2. 特点:数据只能从 data 流向页面

2. 双向数据绑定

  1. 语法:v-mode:value=“xxx” 或简写为 v-model=“xxx”
  2. 特点:数据不仅能从 data 流向页面,还能从页面流向data
    备注:
    1.双向绑定一般都应用在表单类元素上 (如: input、select等)
    2.v-model:value 可以简写为 v-model,因为v-model默认收集的就是value值.
    如:
<body>
    <div id="root">
        <!-- 普通写法 -->
        单向数据绑定:<input type="text" v-bind:value="name">
        <br>
         双向数据绑定:<input type="text" v-model:value="name">
        <!-- 简写 -->
         单向数据绑定:<input type="text" :value="name">
         <br>
          双向数据绑定:<input type="text" v-model="name">
 
    </div>

    <script type="text/javascript">
        //阻止vue 在启动时生成生产提示
        Vue.config.productionTip=false

        //创建Vue实例
        new Vue({
            el:'#root',//el用于指定当前vue实例为那个容器服务,值通常为css选择器字符串
            data:{ //data中用来存储数据,数据供el所指定的容器去使用,值我们暂时先写成一个对象
                name:'vue'
            }
        })
    </script>
</body>

5、el与data的两种写法

1.e1有2种写法

(1).new Vue时候配置el属性。
(2).先创建Vue实例,随后再通过vm.$mount(‘#root’)指定el的值。

2.data有2种写法

(1).对象式
(2).函数式
如何选择: 目前哪种写法都可以,以后学习到组件时,data必须使用函数式,否则会报错

3.一个重要的原则:

出Vue管理的函数,一定不要写箭头函数,一旦写了箭头函数,this就不再是Vue实例了。

如:

<body>
    <div id="root">
       <h1>你好:{{name}}</h1>
    </div>

    <script type="text/javascript">
        //阻止vue 在启动时生成生产提示
        Vue.config.productionTip=false

        //el的两种写法
       const v= new Vue({
      	   //第一种写法    
           // el:'#root', 
           data:{ //data中用来存储数据,数据供el所指定的容器去使用,值我们暂时先写成一个对象
                name:'vue'
            }
        })
        console.log(v)
        v.$mount('#root')//第二种写法



        // data的两种写法
        new Vue({
            el:'#root',//第一种写法     
           // data的第一种写法:对象式
            data:{ 
                name:'vue'
            }
            //data的第二种写法:函数式
            data:function(){
                console.log(this)//此处的this是vue实例对象
                return{
                    name:'Vue'
                }
            }
            //函数式可以简写成
            data(){
                console.log(this)//此处的this是vue实例对象
                return{
                    name:'Vue'
                }
            }
        })
    </script>
</body>

6、 MVVM 模型

  1. M:模型(Model) :对应 data 中的数据
  2. V:视图(View) :模板
  3. VM:视图模型(ViewModel) : Vue 实例对象
    观察发现:
    1.data中所有的属性,最后都出现在了vm身上。
    2.vm身上所有的属性 及 Vue原型上所有属性,在Vue模板中都可以直接使用。
    在这里插入图片描述

7、数据代理

1、 Object.defineProperty方法:

Object.defineProperty方法:是给一个对象添加(定义)属性用的
语法格式:

Object.defineProperty(要添加的对象,添加的属性名,{配置项(基本配置项和高级配置项)})

<body>
    
    <script type="text/javascript">
       let obj={x:100}
       let obj2={y:200}
       Object.defineProperty(obj2,'x',{
        //基本配置项
        value:18,
        enumerable:true,//控制属性是否可以枚举(遍历),默认为false
        writable:true,//控制属性是否可以被修改,默认值为false
        configurable:true,//控制属性是否可以被删除,默认值是false
        //高级配置项
        //当有人读取对象的的x属性是,get函数就会被调用,且返回值就是x的值
        get(){
            return obj.x
        },
        //当有人修改对象的的x属性是,set函数就会被调用,且会收到修改的具体值
        set(value){
            obj.x=value
        }
       })
    </script>
</body>

2、数据代理

1、概念

数据代理:通过一个对象代理对另一个对象中属性的操作(读/写)

2、Vue中的数据代理:

通过vm对象来代理data对象中属性的操作(读/写)

3、Vue中数据代理的好处:

更加方便的操作data中的数据

4、基本原理:

通过object.defineProperty()把data对象中所有属性添加到vm上。
为每一个添加到vm上的属性,都指定一个getter/setter。
在getter/setter内部去操作 (读/写) data中对应的属性

8、事件处理

1、事件的基本使用:

1.使用v-on:xxx 或 @xxx 绑定事件,其中xxx是事件名:
2.事件的回调需要配置在methods对象中,最终会在vm上;
3.methods中配置的函数,不要用箭头函数!否则this就不是vm了,是windows:
4.methods中配置的函数,都是被Vue所管理的函数,this的指向是vm 或 组件实例对象;
5.@click="demo”和 @click="demo($event)” 效果一致,但后者可以传参:

<!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>
    <script type="text/javascript" src="../js/vue.js"></script>
  </head>

  <body>
    <div id="root">
      <h1>Hello,{{name}}</h1>
      <button v-on:click="showInfo">点我提示信息</button>
      <!-- 简写 -->
      <button @click="showInfo1">点我提示信息(不传参数)</button>
      <button @click="showInfo2(66,$event)">点我提示信息( 传参数)</button>
    </div>

    <script type="text/javascript">
      //阻止vue 在启动时生成生产提示
      Vue.config.productionTip = false;

      //创建Vue实例
      const vm = new Vue({
        el: "#root", //el用于指定当前vue实例为那个容器服务,值通常为css选择器字符串
        data: {
          //data中用来存储数据,数据供el所指定的容器去使用,值我们暂时先写成一个对象
          name: "vue"
        },
        methods:{
            showInfo(event){
                console.log(event.target.innerText)
                alert("dddd")
            },
            showInfo2(number,event){
                console.log(event.target.innerText)
                alert("dddd",number)
            }
        }
      });
    </script>
  </body>
</html>

2、事件修饰符

1.prevent: 阻止默认事件 (常用) :
2.stop:阻止事件冒泡 (常用) ;
3.once: 事件只触发一次 (常用) :
4.capture:使用事件的捕获模式;
5.self: 只有event.target是当前操作的元素时才触发事件;
6.passive:事件的默认行为立即执行,无需等待事件回调执行完毕:
注意: 修饰符可以连续写 如 @click.prevent.stop=“showInfo”

<!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>
    <script type="text/javascript" src="../js/vue.js"></script>
    <style>
      * {
        margin-top: 20px;
      }
      .demo1 {
        height: 50px;
        background-color: skyblue;
      }
      .box1 {
        padding: 5px;
        background-color: skyblue;
      }
      .box2 {
        padding: 5px;
        background-color: antiquewhite;
      }
      .list{
        width: 200px;
        height: 200px;
        background-color: bisque;
        overflow: auto;
      }
      li{
        height: 100px;
      }
    </style>
  </head>
  <body>
    <!--
        Vue中的事件修饰符:
        1.prevent: 阻止默认事件 (常用) :
        2.stop:阻止事件冒泡 (常用) ;
        3.once: 事件只触发一次 (常用) :
        4.capture:使用事件的捕获模式;
        5.self: 只有event.target是当前操作的元素时才触发事件;
        6.passive:事件的默认行为立即执行,无需等待事件回调执行完毕:
    -->
    <div id="root">
      <h1>Hello,{{name}}</h1>
      <!-- 阻止默认事件(常用) -->
      <a href="www.baidu.com" @click.prevent="showInfo">点我提示信息</a>
      <!-- 阻止事件冒泡(常用) -->
      <div class="demo1" @click="showInfo">
        <button @click.stop="showInfo">点我提示信息</button>
      </div>
      <!-- once: 事件只触发一次 (常用)  -->
      <button @click.once="showInfo">点我提示信息</button>
      <!-- capture:使用事件的捕获模式 -->
      <div class="box1" @click.capture="showMsg()1">
        div1
        <div class="box2" @click="showMsg(2)">div2</div>
      </div>
      <!-- elf: 只有event.target是当前操作的元素时才触发事件 -->
      <div class="demo1" @click.elf="showInfo">
        <button @click="showInfo">点我提示信息</button>
      </div>
      <!-- passive:事件的默认行为立即执行,无需等待事件回调执行完毕 -->
      <!-- scroll 滚动条滚动事件,wheel鼠标滚轮滚事件-->
      <ul class="list" @scroll="demo">
        <li>1</li>
        <li>2</li>
        <li>3</li>
        <li>4</li>
      </ul>
    </div>

    <script type="text/javascript">
      //阻止vue 在启动时生成生产提示
      Vue.config.productionTip = false;

      //创建Vue实例
      const vm = new Vue({
        el: "#root", //el用于指定当前vue实例为那个容器服务,值通常为css选择器字符串
        data: {
          //data中用来存储数据,数据供el所指定的容器去使用,值我们暂时先写成一个对象
          name: "vue",
        },
        methods: {
          showInfo(event) {
            console.log(event.target.innerText);
            alert("dddd");
          },
          showMsg(msg) {
            alert(msg);
          },
          demo(){
            alert("滚动了")
          }
        },
      });
    </script>
  </body>
</html>

3、键盘事件

1.Vue中常用的按键别名:

回车 => enter
删除 => delete (捕获“删除”和“退格”键)
退出 => esc
空格 => space
换行 => tab(特殊,必须配合keydown使用)
上 => up
下 => down
左 => left
右 => right

2.Vue未提供别名的按键,可以使用按键原始的key值去绑定,但注意要转为kebab-case (短横线命名)
3.系统修饰键(用法特殊) : ctr1、alt、shift、meta

(1),配合keyup使用: 按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发。
(2).配合keydown使用: 正常触发事件。

4.也可以使用keyCode去指定具体的按键(不推荐) @keyup.13=“showInfo”
5.Vue.config.keyCodes.自定义键名 = 键码,可以去定制按键别名

//自定义别名按键
Vue.config.keyCodes.huiche=13

<!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>
    <script type="text/javascript" src="../js/vue.js"></script>
  </head>
  <body>
    <div id="root">
      <h1>Hello,{{name}}</h1>
      <!-- 
        keyup鼠标按下抬起时触发
        keydown鼠标按下时触发
       -->
      <input type="text" placeholder="按下回车提示输入" @keyup.enter="showInfo"/>
    </div>

    <script type="text/javascript">
      //阻止vue 在启动时生成生产提示
      Vue.config.productionTip = false;

      //自定义别名按键
      Vue.config.keyCodes.huiche=13
      //创建Vue实例
      const vm = new Vue({
        el: "#root", //el用于指定当前vue实例为那个容器服务,值通常为css选择器字符串
        data: {
          //data中用来存储数据,数据供el所指定的容器去使用,值我们暂时先写成一个对象
          name: "vue",
        },
        methods: {
          showInfo(e) {
          //  if(e.keyCode!==13) return
            console.log(e.target.value);
          }
        },
      });
    </script>
  </body>
</html>

9、计算属性与监视

1、计算属性 computed

1.定义:要用的属性不存在,要通过已有属性计算得来。
在 computed 对象中定义计算属性。 在页面中使用{{方法名}}来显示计算的结果。
2.原理: 底层借助了objcet,defineproperty方法提供的getter和setter。
3.get函数什么时候执行?

(1).初次读取时会执行一次。
(2).当依赖的数据发生改变时会被再次调用。

4.优势:与methods实现相比,内部有缓存机制(复用) ,效率更高,调试方便。
5.备注:

1.计算属性最终会出现在vm上,直接读取使用即可。
2.如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变

<!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>
    <script type="text/javascript" src="../js/vue.js"></script>
  </head>
  <body>
    <div id="root">
      姓:<input type="text" v-model="firstName"/><br>
      名:<input type="text" v-model="lastName"/><br>
      全名:<span>{{fullName}}</span>
    </div>

    <script type="text/javascript">
      //阻止vue 在启动时生成生产提示
      Vue.config.productionTip = false;

      //自定义别名按键
      Vue.config.keyCodes.huiche=13
      //创建Vue实例
      const vm = new Vue({
        el: "#root", //el用于指定当前vue实例为那个容器服务,值通常为css选择器字符串
        data: {
          //data中用来存储数据,数据供el所指定的容器去使用,值我们暂时先写成一个对象
          firstName: "vue",
          lastName:'三'
        },
        computed:{
          // 完整写法
          fullName:{
            //get的作用:当有人读取fullName时,get就会被调用,且返回值就作为fullName的值
            //get什么时候调用?1、初次读取fullName时。2、所依赖的数据发生变化时
            get(){
                return this.firstName+'-'+this.lastName
            },
            //set什么时候调用?当fullName被修改时
            set(value){
              const arr =value.split('-')
              this.firstName=arr[0]
              this.lastName=arr[1]

            }
          }


          //简写 只考虑读取,不考虑修改时才能用
          fullName(){
            return this.firstName+'-'+this.lastName
          }
        }
      })
    </script>
  </body>
</html>

2、监视属性

  1. 通过 vm 对象的$watch()或 watch 配置来监视指定的属性
  2. 当属性变化时, 回调函数自动调用, 在函数内部进行计算
1、监视属性 watch及简写方式:

1.当被监视的属性变化时,回调函数自动调用,进行相关操作
2.监视的属性必须存在,才能进行监视
3.监视的两种写法:

(1).new Vue时传入watch配置;当创建实例时很明确监视谁时用
(2).通过vm.$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>
    <script type="text/javascript" src="../js/vue.js"></script>
  </head>
  <body>
    <div id="root">
      <h2>今天天气很{{info}}</h2>
      <!-- 绑定事件的时候 @xxx='yyy' yyy可以写一些简单的语句 -->
      <button @click="isHot=!isHot">切换天气1</button>
      <button @click="changWeather">切换天气</button>
    </div>

    <script type="text/javascript">
      //阻止vue 在启动时生成生产提示
      Vue.config.productionTip = false;

      //自定义别名按键
      Vue.config.keyCodes.huiche=13
      //创建Vue实例
      const vm = new Vue({
        el: "#root", //el用于指定当前vue实例为那个容器服务,值通常为css选择器字符串
        data: {
          isHot:true
        },
        methods: {
            changWeather(){
                this.isHot=!this.isHot
            }
        },
        computed:{
            info(){
                return this.isHot?'炎热':'凉爽'
            }
        },
        //当创建实例时很明确监视谁时用
       //正常写法
       isHot:{
            //immediate:false,//初始化时让handler调用一下
            //deep:true,//深度监视
            //handler什么时候调用?当isHot发生改变时
            handler(newValue,oldValue){
                console.log("isHot被修改了",newValue,oldValue)
           },  
        },

        //简写形式 前提是没有其他配置项,只有hander是才能用
        isHot(newValue,oldValue){
          console.log("isHot被修改了",newValue,oldValue)
        },

      });

      //正常写法
        //当创建实例时很不明确监视谁时用
      vm.$watch('isHot',{
        immediate:false,//初始化时让handler调用一下
        //handler什么时候调用?当isHot发生改变时
        handler(newValue,oldValue){
            console.log("isHot被修改了",newValue,oldValue)
        }
      })

      //简写
      vm.$watch('isHot',function(newValue,oldValue){
        console.log("isHot被修改了",newValue,oldValue)
      })
    </script>
  </body>
</html>
1、深度监听 deep

(1).Vue中的watch默认不监测对象内部值的改变 (一层)
(2).配置deep:true可以监测对象内部值改变(多层)。
备注:
(1).Vue自身可以监测对象内部值的改变,但vue提供的watch默认不可以!
(2).使用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>
    <script type="text/javascript" src="../js/vue.js"></script>
  </head>
  <body>
    <div id="root">
      <h2>今天天气很{{info}}</h2>
      <!-- 绑定事件的时候 @xxx='yyy' yyy可以写一些简单的语句 -->
      <button @click="isHot=!isHot">切换天气1</button>
      <button @click="changWeather">切换天气</button>
      <hr/>
      <h3>a的值是:{{numbers.a}}</h3>
      <button @click="numbers.a++">点我让a+1</button>
      <hr/>
      <h3>b的值是:{{numbers.b}}</h3>
      <button @click="numbers.b++">点我让b+1</button>
    </div>

    <script type="text/javascript">
      //阻止vue 在启动时生成生产提示
      Vue.config.productionTip = false;

      //自定义别名按键
      Vue.config.keyCodes.huiche=13
      //创建Vue实例
      const vm = new Vue({
        el: "#root", //el用于指定当前vue实例为那个容器服务,值通常为css选择器字符串
        data: {
          isHot:true,
          numbers:{
            a:1,
            b:1
          }
        },
        methods: {
            changWeather(){
                this.isHot=!this.isHot
            }
        },
        computed:{
            info(){
                return this.isHot?'炎热':'凉爽'
            }
        },
        //当创建实例时很明确监视谁时用
        watch:{
            isHot:{
               //immediate:false,//初始化时让handler调用一下
                //handler什么时候调用?当isHot发生改变时
                handler(newValue,oldValue){
                    console.log("isHot被修改了",newValue,oldValue)
                },  
            },
            //监视多级结构中某个属性的变化
           'number.a':{
              handler(){
                  console.log("a被改变了")
              },  
            },
             //监视多级结构中所有属性的变化
            numbers:{
              deep:true,
              handler(){
                  console.log("a被改变了")
              },
            }
        }
      });
    </script>
  </body>
</html>

3、计算属性computed和监听属性watch的对比

computed利lwatch之间的区别:
1.computed能完成的功能,watch都可以完成。
2.watch能完成的功能,computed不一定能完成,例如: watch可以进行异步操作。
两个重要的小原则:
1.所被Vue管理的函数,最好写成普通函数,这样this的指向才是vm 或 组件实例对象。
2.所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数等),最好写成箭头函数,这样this的指向才是vm 或 组件实例对象。

10、class 与style的绑定

1。class样式

写法:class="xxxxx可以是宁符串、对象、数组。
字符串写法适用于:类名不确定,要动态获取。
对象写法适用于:要绑定多个样式,个数不确定,名字也不确定。
数组写法适用于:要绑定多个样式,个数确定,名宁也确定,但不确定用不用,

2。style样式

:style="(fontsize: xxx]“其中xxx是动态值。
:style=”[a,b]"其中a、b是样式对象。

<!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>
    <script type="text/javascript" src="../js/vue.js"></script>
    <style>
        .basic{

        }
        .happy{

        }
        .sad{

        }
    </style>
   </head>
  <body>
    <div id="root">
        <!-- 绑定class样式--字符串写法,适用于:样式的类名不确定,需要动态指定 -->
        <div class="basic" :class="mood" @click="chanageMood">{{name}}</div>
    
        <!-- 绑定class样式--数组写法,适用于:要绑定的样式个数不确定、名字也不确定 -->
        <div class="basic" :class="classArr">{{name}}</div>I
         <!-- 绑定class样式--对象写法,适用于:要绑定的样式个数确定、名字也确定,但要动态决定用不用 -->
        <div class="basic" :class="calssObj">{{name}}</div>I
        <!-- 绑定style样式--对象写法 -->
        <div class="basic" :style="styleObj">{{name}}</div>I
        
    </div>

    <script type="text/javascript">
      //阻止vue 在启动时生成生产提示
      Vue.config.productionTip = false;


      //创建Vue实例
      const vm = new Vue({
        el: "#root", //el用于指定当前vue实例为那个容器服务,值通常为css选择器字符串
        data: {
          name:'Vue',
          mood:'normal',
          classArr:['style1','style2','style3'],
          classObj:{
            class1:false,
            class2:false
          },
          styleObj:{
            fontSize:'40px',
            color:'red',
            backgroundColor:'orange'
          }
        },
        methods: {
            chanageMood(){
                this.mood='变换后的class名'
            }
        },
      });
    </script>
  </body>
</html>

11、条件渲染

1.V-if

写法:

(1).v-if=“表达式”
(2).V-else-if=“表达式”
(3).v-else=“表达式”
适用于:切换频率较低的场景。
特点:不展示的DOM元素直接被移除。
注意:v-if可以和:v-else-if、v-else一起使用,但要求结构不能被“打断”

2.V-show

写法:v-show="表达式"适用于:切换频率较高的场景。
特点:不展示的DOM元素术被移除,仅仅是使用样式隐藏掉

3.备注:

使用v-if的时,元素可能无法获取到,而使用v-show一定可以获取到。

<!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>
    <script type="text/javascript" src="../js/vue.js"></script>
    <style>
       
    </style>
   </head>
  <body>
    <div id="root">
        <!-- 使用v-show做条件渲染 -->
       <h2 v-show="false">欢迎来到{{name}}</h2>
       <!-- 使用v-if做条件渲染 -->
       <h2 v-if="false">欢迎来到{{name}}</h2>

       <!-- v-else利v-else-if -->
       <div v-if="n === 1">Angular</div>
       <div>@</div>//等于打断了
       <div v-else-if="n === 2">React</div>
       <div v-else-if="n === 3">Vue</div>
       <div v-else>哈哈</div>
       <!-- template模板,不影响代码结构 v-if与template的配合使用 -->
       <template v-if="n===1">
        <h2>dddd</h2>
        <h2>dddd</h2>
        <h2>dddd</h2>
       </template>
    </div>

    <script type="text/javascript">
      //阻止vue 在启动时生成生产提示
      Vue.config.productionTip = false;


      //创建Vue实例
      const vm = new Vue({
        el: "#root", //el用于指定当前vue实例为那个容器服务,值通常为css选择器字符串
        data: {
          name:'Vue',
          }
      });
    </script>
  </body>
</html>

12、列表渲染

v-for指令

1.用于展示列表数据
2.语法: v-for="(item,index) in xxx”:key=“index” 或 v-for="item in xxx”:key=“item.id”
3.可遍历: 数组、对象、字符串(用的很少)、指定次数(用的很少)

<!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>
    <script type="text/javascript" src="../js/vue.js"></script>
    <style>
       
    </style>
   </head>
  <body>
    <div id="root">
        <!-- 遍历数组 -->
        <h2>人员列表(遍历数组)</h2>
       <ul>
        <!-- key的写法一:<li v-for="person in persons" :key="person.id"> -->
        <!-- key的写法二: <li v-for="(person,index) in persons" ::key="index"> -->
        <li v-for="(person,index) in persons" :key="index">
           {{person.name}}-{{person.age}}
        </li>
       </ul>
       <!-- 遍历对象 -->
       <h2>汽车信息(遍历对象)</h2>
       <ul>
         <li v-for="(value,k) in car" :key="k">
           {{k}}----{{value}}
        </li>
       </ul>
       <!-- 遍历字符串 -->
       <h2>测试遍历字符串(用的少)</h2>
       <ul>
         <li v-for="(value,k) in str" :key="k">
           {{k}}----{{value}}
        </li>
       </ul>
        <!-- 遍历指定次数 -->
        <h2>测试遍历指定次数(用的少)</h2>
        <ul>
          <li v-for="(value,k) in 5" :key="k">
            {{k}}----{{value}}
         </li>
        </ul>
    </div>

    <script type="text/javascript">
      //阻止vue 在启动时生成生产提示
      Vue.config.productionTip = false;


      //创建Vue实例
      const vm = new Vue({
        el: "#root", //el用于指定当前vue实例为那个容器服务,值通常为css选择器字符串
        data: {
            persons:[
                {id:'001',name:'张三1',age:'18'},
                {id:'002',name:'张三2',age:'188'},
                {id:'003',name:'张三3',age:'198'}
            ],
            car:{
                name:'奥迪A3',
                price:'18万'
            },
            str:'hello world'
          }
      });
    </script>
  </body>
</html>

1、遍历列表时key的作用(index作为key)

在这里插入图片描述

2、遍历列表时key的作用(id作为key)

f

3、面试题: react、vue中的key有什么作用? (key的内部原理)

1。虚拟DOM中key的作用:

key是虚拟DOM对象的标识,当状态中的数据发生变化时,Vue会根据[新数据]生成[新的虚拟DOM]随后Vue进行[新虚拟DOM] 与[旧虚拟DOM] 的差异比较,比较规则如下:

2.对比规则:

  • (1).旧虚拟DOM中找到了与新虚拟DOM相同的key:

若虚拟DOM中内容没变,直接使用之前的真实DOM!
若虚拟DOM中内容变了,则生成新的真实DOM,随后替换掉贞面中之前的真实DOM.

  • (2).旧虚拟DOM中未找到与新虚拟DOM相同的key
    创建新的真实DOM,随后渲染到到页面。

3。用index作为key可能会引发的问题:

  • 1。若对数据进行:逆序添加、逆序删除等破坏顺序操作:会产生没有必要的真实DOM更新 ==> 界面效果没问题,但效率低。
  • 2。如果结构中还包含输入类的DOM:会产生错误DOM更新 ==> 界面有问题。

4。开发中如何选择key?:

  • 1.最好使用每条数据的唯一标识作为key,比如id、手机号、身份证号、学号等唯一值。
  • 2.如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的。

13、列表过滤

<!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>
    <script type="text/javascript" src="../js/vue.js"></script>
    <style>
       
    </style>
   </head>
  <body>
    <div id="root">
        <!-- 遍历数组 -->
        <h2>人员列表(遍历数组)</h2>
        <input type="text" placeholder="请输入名字" v-model="keyworkd"/>
       <ul>
        <li v-for="(person,index) in filPersons" :key="index">
           {{person.name}}-{{person.age}}
        </li>
       </ul>
    </div>

    <script type="text/javascript">
      //阻止vue 在启动时生成生产提示
      Vue.config.productionTip = false;


      //watch实现
    /*   const vm = new Vue({
        el: "#root", //el用于指定当前vue实例为那个容器服务,值通常为css选择器字符串
        data: {
            keyworkd:'',
            filPersons:[],
            persons:[
                {id:'001',name:'张三1',age:'18'},
                {id:'002',name:'张三2',age:'188'},
                {id:'003',name:'张三3',age:'198'}
            ]
          },
          watch:{
            keyworkd:{
                immediate:true,
                handler(val){
                    this.filPersons = this.persons.filter((p)=>{
                    return p.name.indexOf(val) !== -1
                })
                }
            }
          }
      }); */

      //用computed实现
      const vm = new Vue({
        el: "#root", //el用于指定当前vue实例为那个容器服务,值通常为css选择器字符串
        data: {
            keyworkd:'',
            persons:[
                {id:'001',name:'张三1',age:'18'},
                {id:'002',name:'张三2',age:'188'},
                {id:'003',name:'张三3',age:'198'}
            ]
          },
         computed:{
           filPerons(){
           return this.persons.filter((p)=>{
                return p.name.indexOf(this.keyworkd) !== -1
            })
           }
         }
      });
    </script>
  </body>
</html>

14、列表排序

<!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>
    <script type="text/javascript" src="../js/vue.js"></script>
    <style>
       
    </style>
   </head>
  <body>
    <div id="root">
        <!-- 遍历数组 -->
        <h2>人员列表(遍历数组)</h2>
        <input type="text" placeholder="请输入名字" v-model="keyworkd"/>
        <button @click='sortType=2'>升序</button>
        <button @click='sortType=1'>降序序</button>
        <button @click='sortType=0'>原序</button>
        <ul>
        <li v-for="(person,index) in filPersons" :key="index">
           {{person.name}}-{{person.age}}
        </li>
       </ul>
    </div>

    <script type="text/javascript">
      //阻止vue 在启动时生成生产提示
      Vue.config.productionTip = false;


      //watch实现
    /*   const vm = new Vue({
        el: "#root", //el用于指定当前vue实例为那个容器服务,值通常为css选择器字符串
        data: {
            keyworkd:'',
            filPersons:[],
            persons:[
                {id:'001',name:'张三1',age:'18'},
                {id:'002',name:'张三2',age:'188'},
                {id:'003',name:'张三3',age:'198'}
            ]
          },
          watch:{
            keyworkd:{
                immediate:true,
                handler(val){
                    this.filPersons = this.persons.filter((p)=>{
                    return p.name.indexOf(val) !== -1
                })
                }
            }
          }
      }); */

      //用computed实现
      const vm = new Vue({
        el: "#root", //el用于指定当前vue实例为那个容器服务,值通常为css选择器字符串
        data: {
            keyworkd:'',
            sortType:0,//0代表原顺序 1 降序 2 升序
            persons:[
                {id:'001',name:'张三1',age:'18'},
                {id:'002',name:'张三2',age:'188'},
                {id:'003',name:'张三3',age:'198'}
            ]
          },
         computed:{
           filPerons(){
        		const arr= this.persons.filter((p)=>{
               	 	return p.name.indexOf(this.keyworkd) !== -1
            })
            if(this.sortType){
              arr.sort((p1,p2)=>{
                return this.sortType===1?p2.age-p1.age:p1.age-p2.age
              })
            }
            return arr
           }
         }
      });
    </script>
  </body>
</html>

15、vue检测数据的原理

1,vue会监视data中所有层次数据。
2。如何监测对象中的数据?
通过setter实现监视,且要在new Vue时就传入要监测的数据。

(1).对象中后追加的属性,Vue默认不做响应式处理
(2).如需给后添加的属性做响应式,请使用如下API:
Vue.set(target,propertyName/index, value) 或
vm.$set(target,propertyName/index,value)

3,如何监测数组中的数据?
通过包裹数组更新元素的方法实现,本质就是做了两件事:

(1).调用原生对应的方法对数组进行更新。
(2).重新解析模板,进而更新页面。

4.在Vue修改数组中的某个元素一定要用如下方法:

1.使用这些API:push()、pop()、shift()、unshift()、splice()、sort()、reverse()
2.Vue.set() 或 vm.$set()

特别注意: Vue.set() 和 vm.$set() 不能给vm 或 vm的根数据对象 添加属性

16、收集表单数据

若:<input type="text"/>,则v-model收集的是value值,用户输入的就是value值。
若:<input type="radio"/>,则v-model收集的是value值,且要给标签配置value值
若: <input type="checkbox"/>
	1.没有配置input的value属性,那么收集的就是checked (勾选 or 未勾选,是布尔值)
	2.配置input的value属性:
		(1)v-model的初始值是非数组,那么收集的就是checked(勾选 or 未勾选,是布尔值)
		(2)v-model的初始值是数组,那么收集的的就是value组成的数组
备注:v-model的三个修饰符:
		lazy:失去焦点再收集数据
		number:输入字符串转为有效的数字
		trim:输入首尾空格过滤
<!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>
    <script type="text/javascript" src="../js/vue.js"></script>
    <style>
       
    </style>
   </head>
  <body>
    <div id="root">
        <form @submit='demo'>	//绑定提交事件
            账号: <input type="text" v-model.trim="account"/><br>
            密码:<input type="password" v-model="password"/><br>
            年龄:<input type="number" v-model.number="age"/><br>
            性别:
            男:<input type="radio" name="sex" value="" checked v-model="sex"/>
            女:<input type="radio" name="sex" value="" v-model="sex"/><br>
            爱好:
            学习:<input type="checkbox" value="学习"  v-model="hobby">
            学习1:<input type="checkbox" value="学习1" v-model="hobby">
            学习2:<input type="checkbox" value="学习2" v-model="hobby"><br>
            所属校区:
            <select v-model="city">
                <option value="">请选择校区</option>
                <option value="1">1</option>
                <option value="2">2</option>
                <option value="3">3</option>
            </select>
            <br>
            其他:
            <textarea v-model.lazy="other"></textarea><br>
            <input type="checkbox" v-model="agree"> 阅读并接受<a href="www.baidu.com">用户协议</a>
            <br>
            <button>提交</button>

        </form>
    </div>

    <script type="text/javascript">
      //阻止vue 在启动时生成生产提示
      Vue.config.productionTip = false;

      const vm = new Vue({
        el: "#root", //el用于指定当前vue实例为那个容器服务,值通常为css选择器字符串
        data: {
            account:'',
            password:'',
            sex:'',
            age:'',
            hobby:[],
            city:'',
            other:'',
            agree:''
          },
          methods: {
            demo(){
                console.log(JSON.stringify(this._data))
            }
          },
      });
    </script>
  </body>
</html>

17、过滤器

定义:对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理)

语法:
	1.注册过滤器:Vue.filter(name,callback) 或 new Vue{filters:{}}
	2.使用过滤器:{{ xxx | 过滤器名}} 或 v-bind:属性 = “xxx | 过滤器名
备注:
	1.过滤器也可以接收额外参数、多个过滤器也可以串联
	2.并没有改变原本的数据,是产生新的对应的数据
<!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>
    <script type="text/javascript" src="../js/vue.js"></script>
    <script type="text/javascript" src="../js/dayjs.min.js"></script>
    <style>
        
    </style>
   </head>
  <body>
    <div id="root">
       <h1>显示格式化后的时间</h1>
       <!-- 计算属性实现 -->
        现在是:<h2>{{time}}</h2>
       <!-- methods实现 -->
        现在是:<h2>{{getFmtTime()}}</h2>
       <!-- 过滤器实现 (不传参数)-->
        现在是:<h2>{{time | timeFormater}}</h2>
       <!-- 过滤器实现 (传参数)-->
        现在是:<h2>{{time | timeFormater('yyyy_MM_DD') |mySlice }}</h2>

    </div>

    <script type="text/javascript">
      //阻止vue 在启动时生成生产提示
      Vue.config.productionTip = false;
      //全局过滤器
      Vue.filter('mySlice',function(value){
        return value.slice(0,4)
      })

      const vm = new Vue({
        el: "#root", //el用于指定当前vue实例为那个容器服务,值通常为css选择器字符串
        data: {
           time:1621561377603
          },
        //计算属性实现
          computed:{
            fmtTime(){
                return dayjs(this.time).format('yyyy-MM-DD HH:mm:ss')
            }
          },
         //methods实现  
          methods: {
            getFmtTime(){
                return dayjs(this.time).format('yyyy-MM-DD HH:mm:ss') 
            }
          },
          //过滤器实现,(局部过滤器)
          filters:{ //过滤器配置项
            timeFormater(value,str='yyyy-MM-DD HH:mm:ss'){
                return dayjs(value).format(str) 
            },
            mySlice(value){
                return value.slice(0,4)
            }
          }
      });
    </script>
  </body>
</html>

18、内置指令与自定义指令

1、常用内置指令

v-text指令:

1.作用:向其所在的节点中渲染文本内容。(会替换内容,可以解析html标签)
2.与插值语法的区别: v-text会替换掉节点中的内容,{[xx}}则不会

v-html: 和v-text一样

v-html: 和v-text一样,但能解析html标签
1.作用:向指定节点中渲染包含htm1结构的内容。
2.与插值语法的区别:
(1).v-htm1会替换掉节点中所有的内容,{{xx}}则不会
(2).v-htm1可以识别htm1结构。
3.严重注意: v-html有安全性问题!!! !
(1).在网站上动态渲染任意HTML是非常危险的,容易导致XSS攻击。
(2).一定要在可信的内容上使用v-htm1,永不要用在用户提交的内容上!

v-cloak (没有值) :

1.本质是一个特殊属性,Vue实例创建完毕并接管容器后,会厕掉v-cloak属性。
2.使用css配合v-cloak可以解决网速慢时页面展示出{[xxx}}的问题。

v-once指令:

1.v-once所在节点在初次动态渲染后,就视为静态内容了。
2.以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能。

V-pre(没有值)

1.跳过其所在节点的编译过程。
2.可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译

<!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>
    <script type="text/javascript" src="../js/vue.js"></script>
    <script type="text/javascript" src="../js/dayjs.min.js"></script>
    <style>
        /* v-cloak用 */
        [v-cloak]{
            display: none;
        }
    </style>
   </head>
  <body>
    <div id="root">
        <div v-text="name"></div>
        <div v-html="str"></div>
        <h1 v-cloak>{{name1}}</h1>

        <h2 v-once>初始的n值是:{{n}}</h2>
        <h2>当前的n值是:{{n}}</h2>
        <button @click="n++">点我n+1</button>

        <h1 v-pre>Vue不简单</h1>
    </div>

    <script type="text/javascript">
      //阻止vue 在启动时生成生产提示
      Vue.config.productionTip = false;
   
      const vm = new Vue({
        el: "#root", //el用于指定当前vue实例为那个容器服务,值通常为css选择器字符串
        data: {
           name:'vue',
           str:'<h1>xxxxx</h1>',
           name1:'pppp',
           n:1
          },
       
      });
    </script>
  </body>
</html>

19、自定义指令

1、自定义指令总结:

在这里插入图片描述

2、自定义指令案例(函数式及对象式)

需求1:定义个v-big指令,莉v-text功能类似,但会把绑定的数值放大10倍。
需求2: 定义一个v-fbind指令,和v-bind功能类似,但可以让其所绑定的input元素默认获取焦点。

<!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>
    <script type="text/javascript" src="../js/vue.js"></script>
    <script type="text/javascript" src="../js/dayjs.min.js"></script>
    <style>
    </style>
   </head>
  <body>
    <div id="root">
       <h1>当前的n值是:<span v-text="n"></span></h1>
       <h1>放大10倍后的值<span v-big="n"></span></h1>
       <button @click="n++">点我n+1</button>
    </div>

    <script type="text/javascript">
      //阻止vue 在启动时生成生产提示
      Vue.config.productionTip = false;
      //自定义全局指令
      Vue.directives('fbind',{
            //指令与元素成功绑定时
              bind(element,binding){
                  element.value=binding.value
              },
              //指令所在元素被插入页面时
              inserted(element,binding){
                element.focus()
              },
              //指令所在的模板被重新解析时
              update(element,binding){
                element.value=binding.value
              }
           })

      const vm = new Vue({
        el: "#root", //el用于指定当前vue实例为那个容器服务,值通常为css选择器字符串
        data: {
           n:1
          },
          //自定义big指令 局部指令
          directives:{
            //big何时调用?1、指令与元素成功绑定时 2、指令所在的模板被重新解析时(数据发生改变时)
            //函数式
            big(element,binding){
               element.innerText=binding.value*10 
            },
            //对象式
            fbind:{
            //指令与元素成功绑定时
              bind(element,binding){
                  element.value=binding.value
              },
              //指令所在元素被插入页面时
              inserted(element,binding){
                element.focus()
              },
              //指令所在的模板被重新解析时
              update(element,binding){
                element.value=binding.value
              }
           }
          }
       
      });
    </script>
  </body>
</html>

20、生命周期

1.又名:生命周期回调函数、生命周期函数、生命周期钩子。
2.是什么:Vue在关键时刻帮我们调用的一些特殊名称的函数。
3.生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的。
4.生命周期函数中的this指向是vm 或 组件实例对象。

1、vm的生命周期

将要创建:调用beforeCreate函数
创建完毕:调用created函数。
将要挂载:调用beforeMount函数。
挂载完毕:调用mounted函数。
将要更新:调用beforeUpdate函数
更新完毕:调用updated函数。
将要销毁:调用beforeDestroy函数
销毁完毕:调用destroyed函数。
在这里插入图片描述

2、常用的生命周期钩子:

1.mounted: 发送ajax请求、启动定时器、绑定自定义事件、订阅消息等[初始化操作]
2.beforeDestroy: 清除定时器、解绑自定义事件、取消订阅消息等[收尾工作]。

3、关于销毁Vue实例

1.销毁后借助Vue开发者工具看不到任何信息。
2.销毁后自定义事件会失效,但原生DOM事件依然有效。
3.一般不会beforeDestroy操作数据,因为即便操作数据,也不会再触发更新流程了。

常用指令总结

	v-bind:单向绑定解析表达式,可简写为 :xxx
	v-model:双向数据绑定
	v-for:遍历数组/对象/字符串
	v-on:绑定事件监听,可简写为@
	v-if:条件渲染(动态控制节点是否存存在)
	V-else:条件渲染(动态控制节点是否存存在)
	V-show:条件渲染 (动态控制节点是否展示)
	v-text:向其所在的标签插入文本
	v-text
		1.作用:向其所在的节点中渲染文本内容。(会替换内容)
		2.与插值语法的区别: v-text会替换掉节点中的内容,{[xx}}则不会
		
	v-html: 和v-text一样,但能解析html标签
		1.作用:向指定节点中渲染包含htm1结构的内容。
		2.与插值语法的区别:
			(1).v-htm1会替换掉节点中所有的内容,{{xx}}则不会
			(2).v-htm1可以识别htm1结构。
		3.严重注意: v-html有安全性问题!!! !
			(1).在网站上动态渲染任意HTML是非常危险的,容易导致XSS攻击。
			(2).一定要在可信的内容上使用v-htm1,永不要用在用户提交的内容上!
			
	v-cloak (没有值) :
		1.本质是一个特殊属性,Vue实例创建完毕并接管容器后,会厕掉v-cloak属性。
		2.使用css配合v-cloak可以解决网速慢时页面展示出{[xxx}}的问题。
		
	v-once(没有值)
		1.v-once所在节点在初次动态渲染后,就视为静态内容了。
		2.以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能。
	
	V-pre(没有值)
		1.跳过其所在节点的编译过程。
		2.可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译



第 2 章:Vue 组件化编程

1、 模块与组件、模块化与组件化

1. 模块

1.理解: 向外提供特定功能的 js 程序, 一般就是一个 js 文件
2. 为什么: js 文件很多很复杂
3. 作用: 复用 js, 简化 js 的编写, 提高 js 运行效率

2. 组件

1.理解: 用来实现局部(特定)功能效果代码和资源的集合(html/css/js/image……)
2. 为什么: 一个界面的功能很复杂
3. 作用: 复用编码, 简化项目编码, 提高运行效率

3. 模块化

当应用中的 js 都以模块来编写的, 那这个应用就是一个模块化的应用。

4. 组件化

当应用中的功能都是多组件的方式来编写的, 那这个应用就是一个组件化的应用,。

2、非单文件组件

1、概念

非单文件组件:一个文件中包含有n个组件
单文件组件:一个文件中只包含有1一个组件

3、Vue中使用组件的三大步骤:

一、定义组件(创建组件)
二、注册组件
三、使用组体(写组件标签)

一、 如何定义一个组件?

使用Vue.extend(options)创建,其中options和new Vue(options)时传入的那个options几乎一样,但也有点区别:区别如下:

1、el不要写,为什么? 最终所有的组件都要经过一个vm的管理,由vm中的el决定服务哪个容器。
2、data必须写成函数,为什么? 避免组件被复用时,数据存在引用关系。

备注:使用template可以配置组件结构。

二、如何注册组件?

1.局部注册: 靠new Vue的时候传入components选项
2.全局注册:靠Vue.component('组件名,组件)

三、编写组件标签:

<school></schoo1>

例如:

<!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>
    <script type="text/javascript" src="../js/vue.js"></script>
    <script type="text/javascript" src="../js/dayjs.min.js"></script>
    <style></style>
  </head>
  <body>
    <div id="root">
      <!-- 第三步:编写组件标签 -->
        <school></school>
    </div>

    <script type="text/javascript">
      //阻止vue 在启动时生成生产提示
      Vue.config.productionTip = false;

      //第一步:创建school组件
      const school = Vue.extend({
        // el: "#root",//组件定义时一定不要写el配置项,因为最终所有的组件都要被一个vm管理,由vm决定服务于哪个容器
        data() {
          return {
            schoolName: "学校",
            address: "xxxx",
          };
        },
        template:`
            <div>
                <h2>学习名称:{{schoolName}}</h2>
                <h2>学习地址:{{address}}</h2>
                <button @click="showName">点我提示学校名</button>
            </div>
         `,
         methods: {
            showName(){
                alert(this.schoolName)
            }
         },
      });

      //第一步:创建hello组件
      const he = VUe.extend({
        name:'cy',//可以使用name配置项指定组件在开发者工具中呈现的名字。
        template:`
          <div>
            <h2>你好呀!{{name}}</h2>  
          </div>
        `,
        data(){
          return {
            name:'TOM'
          }
        }
      })
      
      //第二步:注册组件(全局注册)
      Vue.component('hello',he)
     
      new Vue({
        el: "#root", //el用于指定当前vue实例为那个容器服务,值通常为css选择器字符串
        //第二步:注册组件(局部注册)
        components:{
            school:school
            //可以简写
            // school
        }
      })
  
    </script>
  </body>
</html>

4、组件的几个注意事项

1.关于组件名:

一个单词组成:

第一种写法(首字母小写):school
第二种写法(首字母大写): school

多个单词组成:

第一种写法(kebab-case命名): my-school
第二种写法(CamelCase命名): MySchool (需要Vue脚手架支持)

备注:

(1).组件名尽可能回避HTML中己有的元素名称,例如: h2、H2都不行。
(2).可以使用name配置项指定组件在开发者工具中呈现的名字。

2.关于组件标签:

第一种写法:
第二种写法:
备注:不用使用脚手架时,会导致后续组件不能渲染

3.一个简写方式:

options:表示配置项 { }

const school = Vue,extend(options) 可简写: const school = options

//第一步:创建hello组件
  const he ={
      name:'cy',//可以使用name配置项指定组件在开发者工具中呈现的名字。
      template:`
        <div>
          <h2>你好呀!{{name}}</h2>  
        </div>
      `,
      data(){
        return {
          name:'TOM'
        }
      }
    }

5、组件的嵌套

<!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>
    <script type="text/javascript" src="../js/vue.js"></script>
    <script type="text/javascript" src="../js/dayjs.min.js"></script>
    <style></style>
  </head>
  <body>
    <div id="root">
      <!-- 第三步:编写组件标签 -->
        <school></school>
      <hr />
    </div>

    <script type="text/javascript">
      //阻止vue 在启动时生成生产提示
      Vue.config.productionTip = false;

       //第一步:创建student组件
       const student = Vue.extend({
        data() {
          return {
            studentname: "名字",
            age: 18,
          };
        },
        template:`
            <div>
                <h2>学生名称:{{studentname}}</h2>
                <h2>学习年龄:{{age}}</h2>
            </div>
        `,
      });

      //第一步:创建school组件
      const school = Vue.extend({
        // el: "#root",//组件定义时一定不要写el配置项,因为最终所有的组件都要被一个vm管理,由vm决定服务于哪个容器
        data() {
          return {
            schoolName: "学校",
            address: "xxxx",
          };
        },
        template:`
            <div>
                <h2>学习名称:{{schoolName}}</h2>
                <h2>学习地址:{{address}}</h2>
                <student></student>	 //引入student组件
                <button @click="showName">点我提示学校名</button>
            </div>
         `,
         methods: {
            showName(){
                alert(this.schoolName)
            }
         },
         //注册组件(局部注册)嵌套组件
         components:{
            student
         }
      });
     
      new Vue({
        el: "#root", //el用于指定当前vue实例为那个容器服务,值通常为css选择器字符串
        //第二步:注册组件(局部注册)
        components:{
            school:school,
            //可以简写
            // school,
        }
      })
  
    </script>
  </body>
</html>

6、关丁VueComponent:

1.school组件本质是一个名为VueComponent的构造函数,且不是程序员定义的,是Vue,extend生成的。
2.我们只需要写<school/>或<schoolx/school>,Vue解析时会帮我们创建school组件的实例对象,即Vue帮我们执行的: new VueComponent(options)。
3.特别注意: 每次调用Vue.extend,返回的都是一个全新的VueComponent!!!!
4.关于this指向:
	(1).组件配置中:
		data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是[VueComponent实例对象]
	(2).new Vue()配置中:
		data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是[Vue实例对象]
5.VueComponent的实例对象,以后简称vc (也可称之为: 组件实例对象)。
	Vue的实例对象,以后简称vm。

7、一个重要的内置关系:

1.一个重要的内置关系:

VueComponent.prototype._proto_=== Vue.prototype

2.为什么要有这个关系:

让组件实例对象 (vc) 可以访问到 Vue原型上的属性、方法。

8、单文件组件

1. 一个.vue 文件的组成(3 个部分)

	// 1、模板页面
	<template>
	  <!-- 组件的结构 -->
	  <div>
	
	  </div>
	</template>
	// 2、JS模板对象
	<script>
	    //组件交互相关的代码(数据、方法)
	</script>
	// 3、样式
	<style >
	    /* //组件的样式 */
	</style>

如:School.vue

// 1、模板页面
<template>
  <!-- 组件的结构 -->
    <div class="demo">
        <h2>学习名称:{{schoolName}}</h2>
        <h2>学习地址:{{address}}</h2>
        <button @click="showName">点我提示学校名</button>
    </div>
</template>
// 2、JS模板对象
<script>
    //组件交互相关的代码(数据、方法)
     export default {
        name:'School',
        data() {
          return {
            schoolName: "学校",
            address: "xxxx",
          };
        },
         methods: {
            showName(){
                alert(this.schoolName)
            }
         },
      }
</script>

// 3、样式
<style >
    /* //组件的样式 */
    .demo{
        background-color: aliceblue;
    }
</style>

Student.vue

// 1、模板页面
<template>
  <!-- 组件的结构 -->
    <div class="demo">
        <h2>学生姓名{{name}}</h2>
        <h2>学生年龄{{age}}</h2>
        <button @click="showName">点我提示学名字</button>
    </div>
</template>
// 2、JS模板对象
<script>
    //组件交互相关的代码(数据、方法)
     export default {
        name:'School',
        data() {
          return {
            name: "谢谢谢",
            age: 19,
          };
        },
         methods: {
            showName(){
                alert(this.schoolName)
            }
         },
      }
</script>

// 3、样式
<style >
    /* //组件的样式 */
    .demo{
        background-color: aliceblue;
    }
</style>

App.vue

<template>
  <div>
    <School></School>
    <Student></Student>
  </div>
</template>

<script>
    //引入组件
    import School from '01初始vue/单文件组件/School'
    import Student from '01初始vue/单文件组件/Student'

    export default {
      name:'App',
      //注册组件
      components:{
        School,
        Student
      }
    }
</script>

<style>

</style>

main.js

import App from '01初始vue/单文件组件/App'

new Vue({
    el:'#root',
    template:`<App></App>`,
    components:{
        App
    }
})

index.html

<!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="root"></div>
    <!-- <script type="text/javaScript" src="../../js/vue.js"></script>
    <script type="text/javaScript" src="./main.js"></script> -->
</body>
</html>

第 3 章:使用 Vue 脚手架

1 初始化脚手架

1、说明

1.Vue 脚手架是 Vue 官方提供的标准化开发工具(开发平台)。
2. 最新的版本是 4.x。
3. 文档: https://cli.vuejs.org/zh/。

2 、具体步骤

第一步(仅第一次执行):全局安装@vue/cli。
npm install -g @vue/cli
第二步:切换到你要创建项目的目录,然后使用命令创建项目
vue create xxxx
第三步:启动项目
npm run serve
备注:

1.如出现下载缓慢请配置 npm 淘宝镜像:

npm config set registry https://registry.npm.taobao.org

2.Vue 脚手架隐藏了所有 webpack 相关的配置,若想查看具体的webpakc 配置,请执行:

vue inspect > output.js

3 模板项目的结构

在这里插入图片描述

4、关于不同版本的Vue:

1.vue.js与vue.runtime.xxx.js的区别:

(1).vue.js是完整版的Vue,包含: 楼心功能+模板解析器。
(2).vue.runtime.xxx.js是运行版的Vue,只包含: 核心功能;没有模板解析器.

2.因为vue.runtime.xxx.js没有模板解析器,所以不能使用template配置项,需要使用render函数接收到的createElement函数去指定具体内容。

2 ref 与 props

1、ref

1.被用来给元素或子组件注册引用信息I(id的替代者)
2.应用在htm1标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象 (vc)
3.使用方式:

打标识: <h1 ref="xxx">.....</h1><School ref="xxx"></Schoo1>
获取: this.$refs.xxx

如:

<template>
  <div>
    <School></School>
    <Student ref="stud"></Student>
    <h1 v-text="msg" ref="title"></h1>
    <button @click="showDOM">点我输入h2的DOM元素</button>
  </div>
</template>

<script>
    //引入组件
   import School from '@/components/School'
   import Student from '@/components/Student'

    export default {
      name:'App',
      //注册组件
      components:{
        School,
        Student
      },
      data(){
        return{
           msg:'欢迎回来!'
        }
      },
      methods:{
        showDOM(){
              console.log(this.$refs.title) //真实的DOM元素<h1>欢迎回来!</h1>
               console.log(this.$refs.stud) //VC的组件实例对象
            }
      }
       
    }
</script>

<style>

</style>

2、props

1.作用:用于父组件给子组件传递数据
2. 读取方式一: 只指定名称

props: ['name', 'age', 'setName']

3.读取方式二: 指定名称和类型

props: {
	name: String,
	age: Number, 
	setNmae: Function
}

4.读取方式三: 指定名称/类型/必要性/默认值

props: {
	name: {type: String, required: true, default:xxx}, 
}

5.传递数据:

<Student name="xxx" age=18/>

备注: props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告,若业务需求确实需要修改,那么请复制props的内容到data中一份,然后去修改data中的数据。

如:
传值

<template>
  <div>
    <Student name='学生1' age='19'/> //传值

  </div>
</template>

<script>
    //引入组件
   import Student from '@/components/Student'

    export default {
      name:'App',
      //注册组件
      components:{
        Student
      }
    }
</script>

<style>

</style>

接收值

// 1、模板页面
<template>
  <!-- 组件的结构 -->
    <div class="demo" >
        <h2 v-text="msg"></h2>
        <h2>学生姓名:{{name}}</h2>
        <h2>学生年龄:{{myAge}}</h2>
        <button @click="updateage">点我修改年龄</button>      
    </div>
</template>
// 2、JS模板对象
<script>
    //组件交互相关的代码(数据、方法)
     export default {
        name:'Student',
        data() {
          return {
            msg:'我是学生',
            myAge:this.age
          };
        },
        methods:{
          updateage(){
            this.myAge++
          }
        },
        //方式一:简单接受
        // props:['name','age'] 

        //方式二:接收的同时对数据进行类型限制
       /*  props:{
          name:String,
          age:Number
        } */

        //方式三:接收的同时指定名称/类型/必要性/默认值
        props:{
          name:{
            type:Stirng,//name的类型是字符串
            required:true //name是必要的
          },
          age:{
            type:Number,
            default:99//默认值
          }
        }
      }
</script>

// 3、样式
<style >
    /* //组件的样式 */
    .demo{
        background-color: aliceblue;
    }
</style>

3 混入minxin

功能:可以把多个组件共用的配置提取成一个混入对象
使用方式:
第一步自定义混合,例如:

{
	data()(....},
	methods:{....]
}

第二步使用混入,例如:
(1).全局混入: Vue.mixin(xxx)
(2).局部混入: mixins:[‘xxx’]

如:
第一步:自定义混合mixin.js文件

export const hunhe={
    methods: {
        showName(){
            alert(this.schoolName)
        }
     }
}

第二步:使用混入

<script>
  // 局部引用
    import {hunhe} from '../minxin'
    //组件交互相关的代码(数据、方法)
     export default {
        name:'School',
        data() {
          return {
            schoolName: "学校",
            address: "xxxx",
          };
        },
        mixins:[hunhe]
      }
</script>

再main.js中全局引入

//引入Vue
import Vue from 'vue'
//引入App
import App from './App.vue'
import { hunhe } from './minxin'
//关闭Vue的生产提示
Vue.config.productionTip = false

//全局引入
Vue.mixin(hunhe)

//创建VM
new Vue({
  render: h => h(App),
}).$mount('#app')

4 插件

1、功能:

用于增强Vue

2、本质:

包含install方法的一个对象,install的第一个参数是Vue,第二个以后的参数是插件使用者传递的数据。

3、定义插件:

对象.install = function (Vue, options) {
	// 1。添加全局过滤器
	Vue.filter(....)
	// 2.添加全局指令
	Vue.directive(....)
	// 3。配置全局混入(合
	)Vue.mixin(....)
	// 4。添加实例方法
	Vue.prototype.$myMethod = function () [...}
	Vue.prototype.$myProperty = xxxx
}

如:

export default {
	install(Vue,x,y,z){
		console.log(x,y,z)
		//全局过滤器
		Vue.filter('mySlice',function(value){
			return value.slice(0,4)
		})

		//定义全局指令
		Vue.directive('fbind',{
			//指令与元素成功绑定时(一上来)
			bind(element,binding){
				element.value = binding.value
			},
			//指令所在元素被插入页面时
			inserted(element,binding){
				element.focus()
			},
			//指令所在的模板被重新解析时
			update(element,binding){
				element.value = binding.value
			}
		})

		//定义混入
		Vue.mixin({
			data() {
				return {
					x:100,
					y:200
				}
			},
		})

		//给Vue原型上添加一个方法(vm和vc就都能用了)
		Vue.prototype.hello = ()=>{alert('你好啊')}
	}
}

4、再main.js中使用插件:

Vue.use(插件名)

如:

//引入Vue
import Vue from 'vue'
//引入App
import App from './App.vue'
//关闭Vue的生产提示
Vue.config.productionTip = false

//引入插件
import plugins from './plugins'
//使用插件
Vue.use(plugins)


//创建VM
new Vue({
  render: h => h(App),
}).$mount('#app')

5、scoped样式

作用:让样式在局部生效,防止冲突。
写法:

style scoped>
	//样式
</style>

6、组件的自定义事件在这里插入图片描述

App.vue

<template>
  <div>
    <!-- 通过父组件给子组件传递函数类型的props实现:子给父传递数据 -->
    <School :getSchoolName='getSchoolName'/>

    <!-- 写法一:使用@或v-on:通过父组件给子组件绑定一个自定义事件实现:子给父传递数据 -->
    <Student v-on:cy="getStudentName"/>
    <!-- 只在第一次起作用 -->
    <Student v-on:cy.once="getStudentName"/>
    <!-- 写法二:使用ref:通过父组件给子组件绑定一个自定义事件实现:子给父传递数据 -->
    <Student ref='student'/>
  </div>
</template>

<script>
    //引入组件
   import Student from '@/components/Student'
   import School from './components/School.vue'

    export default {
      name:'App',
      //注册组件
      components:{
        Student,
        School,
      },
      methods:{
        getSchoolName(name){
          return this.name
        },
        getStudentName(name){
          console.log("cy事件被触发了")
          return this.name
        }
      },
      mounted(){
        // 绑定自定义事件
        this.$refs.Student.$on('cy',this.getStudentName)
        // 只在第一次起作用
        this.$refs.Student.$once('cy',this.getStudentName)
      }
    }
</script>

<style>

</style>

Student.vue

<template>
	<div class="test">
		<h2>学生姓名:{{name}}</h2>
		<h2>学生性别:{{sex}}</h2>
		<button @click="sendStudentName">把学生名给App</button>
		<button @click="unbind">解绑cy事件</button>
		<button @click="death">销毁了当前组件就的实例</button>
	</div>
</template>

<script>
	export default {
		name:'Student',
		data() {
			return {
				name:'张三',
				sex:'男'
			}
		},
		methods:{
			sendStudentName(){
				//触发Student组件实例身上的cy事件
				this.$emit('cy',this.name)
			},
			unbind(){
				//解绑自定义事件(只使用与一个自定义事件)
				this.$off('cy')
				//解绑多个自定义事件
				this.$off(['cy','自定义事件2','xxx'])
				this.$off()//所有的自定义事件全部解绑
			},
			death(){
				this.$destroy()//销毁了当前组件就的实例,销毁后该组件实例的自定义事件全部不奏效
			}
		}
	}
</script>

<style scoped>
	.test{
		background-color: bisque;
	}
</style>

7、全局事件总线 (GlobalEventBus):任意组件间通信

1.一种组件间通信的方式,适用于任意组件间通信。
2.安装全局事件总线:

new Vue({
	。。。。。。。
	beforeCreate(){
	Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm
	},
		......
})

3.使用事件总线:

  1. 接收数据:A组件想接收数据,则在A组件中给Sbus绑定自定义事件,事件的回调留在A组件自身.

    methods(){
    	demo(data){......}
    }
    ....
    mounted() {
    	this.$bus.$on("xxxx',this .demo)
    }
    
  2. 提供数据:

     this.$bus.$emit("xxxx’,数据)
    

4.最好在beforeDestroy钩子中,用$off去解绑当前组件所用到的事件。

如:
在main.js中 安装全局事件总线

//引入Vue
import Vue from 'vue'
//引入App
import App from './App.vue'
//关闭Vue的生产提示
Vue.config.productionTip = false

//创建VM
new Vue({
  render: h => h(App),
  beforeCreate(){
    Vue.prototype.$bus=this //安装全局事件总线
  }
}).$mount('#app')

将学生名传递给学校组件
Student.vue

<template>
	<div class="test">
		<h2>学生姓名:{{name}}</h2>
		<h2>学生性别:{{sex}}</h2>
		<button @click="sendStudentName">把学生名给School组件</button>
	</div>
</template>

<script>
	export default {
		name:'Student',
		data() {
			return {
				name:'张三',
				sex:'男'
			}
		},
		methods:{
			sendStudentName(){
				//触发Student组件实例身上的cy事件
				this.$bus.$emit('hello',this.name)
			}
		}
	}
</script>

<style scoped>
	.test{
		background-color: bisque;
	}
</style>

School.vue组件

<template>
	<div class="demo">
		<h2>学校名称:{{name | mySlice}}</h2>
		<h2>学校地址:{{address}}</h2>
	</div>
</template>

<script>
	export default {
		name:'School',
		data() {
			return {
				name:'尚硅谷atguigu',
				address:'北京',
			}
		},
		mounted(){
			this.$bus.$on("hell",(data)=>{
				console.log("我是School组件,收到了数据",data)
			})
		},
		//beforeDestroy钩子中,用$off去解绑当前组件所用到的事件。
		beforeDestroy(){
			this.$bus.$off('hello')
		}
	}
</script>
<style >
	.demo{
		background-color: aqua;
	}
</style>

8、消息订阅与发布

1 理解

  1. 这种方式的思想与全局事件总线很相似
  2. 它包含以下操作:
    (1) 订阅消息 --对应绑定事件监听
    (2) 发布消息 --分发事件
    (3) 取消消息订阅 --解绑事件监听
  3. 需要引入一个消息订阅与发布的第三方实现库: PubSubJS

2 使用 PubSubJS

  1. 在线文档: https://github.com/mroderick/PubSubJS
  2. 下载: npm install -S pubsub-js
  3. 相关语法
(1) import PubSub from 'pubsub-js' // 引入
(2) PubSub.subscribe(‘msgName’, functon(msgName, data){ })
(3) PubSub.publish(‘msgName’, data): 发布消息, 触发订阅的回调函数调用
(4) PubSub.unsubscribe(token): 取消消息的订阅

3、总结:

在这里插入图片描述
如:
Student.vue

<template>
	<div class="test">
		<h2>学生姓名:{{name}}</h2>
		<h2>学生性别:{{sex}}</h2>
		<button @click="sendStudentName">把学生名给School组件</button>
	</div>
</template>

<script>
	import pubsub from 'pubsub-js'
	export default {
		name:'Student',
		data() {
			return {
				name:'张三',
				sex:'男'
			}
		},
		methods:{
			sendStudentName(){
				// //触发Student组件实例身上的cy事件
				// this.$bus.$emit('hello',this.name)
				pubsub.publish('hello',666)
			}
		}
	}
</script>

<style scoped>
	.test{
		background-color: bisque;
	}
</style>

School.vue

<template>
	<div class="demo">
		<h2>学校名称:{{name | mySlice}}</h2>
		<h2>学校地址:{{address}}</h2>
	</div>
</template>

<script>
	// 引入
	import pubsub from 'pubsub-js'
	export default {
		name:'School',
		data() {
			return {
				name:'尚硅谷atguigu',
				address:'北京',
			}
		},
		mounted(){
			// this.$bus.$on("hell",(data)=>{
			// 	console.log("我是School组件,收到了数据",data)
			// })
			this.pubId=	pubsub.subscribe('hello',(msgName,data)=>{
				console.log("有人发布了hello消息,hello消息的回调执行了",msgName,data)
			})
		},
		beforeDestroy(){
			// this.$bus.$off('hello')
			pubsub.unsubscribe(this.pubId)
		}
	}
</script>
<style >
	.demo{
		background-color: aqua;
	}
</style>

nextTick

1.语法: this.$nextTick(回调函数)
2.作用: 在下一次 DOM 更新结束后执行其指定的回调
3.什么时候用: 当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行.

9 、Vue封装的过度与动画

1、作用:在插入、跟新或移除DOM元素时,在合适的时候给元素添加样式类名
2、图示
在这里插入图片描述
3、写法

  1. 准备好样式:
元素进入的样式
	1、v-enter:进入的起点
	2、v-enter-axtive:进入过程中
	3、v-enter-to:进入的终点
元素离开的样式
	1、v-leave:离开的起点
	2、v-leave-active:离开过程中
	3、v-leave-to:离开的终点

  1. 使用包裹要过渡的元素,并配置name属性:
  <transition appear name='hello'>
        <h1 v-show="isShow">你好呀!</h1>
  </transition>
  1. 备注:若有多个元素需要过度,则需要使用 transition-group,且每个元素都要指定key值
    如:
<template>
    <div>
        <button @click="isShow =!isShow">显示/隐藏</button>
        <!-- <h1 v-show="isShow" class="come">你好呀!</h1> -->
        <transition appear name='hello'>
             <h1 v-show="isShow">你好呀!</h1>
        </transition>
        <!-- transition-group 可以实现多个元素过度 -->
        <transition-group appear name='hello '>
            <h1 v-show="isShow" key="1">你好呀!</h1>
            <h1 v-show="isShow" key='2'>辰逸</h1>
        </transition-group>
    </div>
</template>
<script>


export default ({
    name:'Test2',
    data(){
        return{
            isShow:true
        }
    }
})
</script>

<style scoped>
    h1{
        background-color: orange;
        /* transition: 05s linear; */
    }
    //封装的过度
    /* 进入的起点、离开的终点*/
   .hello-enter,.hello-leave-to{
        transform: translateX(-100%);
   }
   /* 进入的终点 、离开的起点*/
   .hello-enter-to,.hello-leave{
        transform: translateX(0);
   }
   .hello-enter-active,.hello-leave-active{
        transition: 05s linear;
   }
	
	//封装的动画,过度和动画使用一个就可以
  .v-enter-active{
        animation:cy 1s
    }
    .v-leave-active{
       animation:  cy 1s reverse; 
    }
    @keyframes cy{
        from{
            transform: translateX(-100px);
        }
        to{
              transform: translateX(-0px);
        }
    }

</style>
  1. 第三方动画库
    https://www.npmjs.com/网站上搜索animate.css
<template>
    <div>
        <button @click="isShow =!isShow">显示/隐藏</button>
        <!-- <h1 v-show="isShow" class="come">你好呀!</h1> -->
        <transition appear name='hello'>
             <h1 v-show="isShow">你好呀!</h1>
        </transition>
        <!-- transition-group 可以实现多个元素过度 -->
        <transition-group 
            appear 
            name='animate_animated animate_bounce'
            enter-active-class="animate_swing"
            leave-active-class="animate_backOutUp"
            >
            <h1 v-show="isShow" key="1">你好呀!</h1>
            <h1 v-show="isShow" key='2'>辰逸</h1>
        </transition-group>
    </div>
</template>
<script>
//引入animate
import 'animate.css'
export default ({
    name:'Test2',
    data(){
        return{
            isShow:true
        }
    }
})
</script>

<style scoped>
    h1{
        background-color: orange;
        /* transition: 05s linear; */
    }
   

</style>

第 4 章:Vue 中的 ajax

1 解决开发环境 Ajax 跨域问题(代理服务器)

在这里插入图片描述

2、vue脚手架配置代理

方法一
在vue.config.s中添加如下配置:

  devServer:{
    proxy:'http://localhost:5000'
  }

说明:

  1. 优点:配置简单,请求资源时直接发给前端 (8080) 即可。
  2. 缺点:不能配置多个代理,不能灵活的控制请求是否走代理
  3. 工作方式:若按照上述配置代理,当请求了前端不存在的资源时,那么该请求会转发给服务器(优先匹配前端资源)

方法二:
编写vue.config.js配置具体代理规则

 devServer: {
    proxy: {
      '/cy': { //匹配所有以'/cy'开头的请求路径
        target: 'http://localhost:5000',//代理目标的基础路径
        pathRewrite:{'^/cy':''},  //去掉请求地址中的/cy
        ws: true, //用于支持websocket
        changeOrigin: true //用于控制请求头中的host值
      },
      '/demo': {//匹配所有以'/demo'开头的请求路径
        target: 'http://localhost:5001',//代理目标的基础路径
        pathRewrite:{'^/demo':''},//去掉请求地址中的/demo
        ws: true, //用于支持websocket
        changeOrigin: true //用于控制请求头中的host值 为true时请求头的host为:localhost:5000,为false时,localhost:8080 默认为true
      },
    }
  }

如:
vue.config.js

const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
  transpileDependencies: true,
  lintOnSave:false , //关闭语法检查
  //开启代理服务器(方式一)
 /*  devServer:{
    proxy:'http://localhost:5000'
  }, */

  //开启代理服务器(方式二)
  devServer: {
    proxy: {
      '/cy': {
        target: 'http://localhost:5000',
        pathRewrite:{'^/cy':''},  //去掉请求地址中的/cy
        ws: true, //用于支持websocket
        changeOrigin: true //用于控制请求头中的host值
      },
      '/demo': {
        target: 'http://localhost:5001',
        pathRewrite:{'^/demo':''},//去掉请求地址中的/demo
        ws: true, //用于支持websocket
        changeOrigin: true //用于控制请求头中的host值
      },
    }
  }
})

App.vue

<template>
  <div>
    <button @click="getSutdents">获取学生信息</button>
    <button @click="getCars">获取汽车信息</button>
  </div>
  <!-- 
    http://localhost:5000/students
    http://localhost:5001/cars
     -->
</template>

<script>

    import axios from 'axios'
    export default {
      name:'App',
      methods:{
        getSutdents(){
          axios.get('http://localhost:8080/cy/students').then(
            response=>{
              console.log("请求成功了",response.data)
            },
            error=>{
              console.log('请求失败了',error.message)
            }
          )
        },
        getCars(){
          axios.get('http://localhost:8080/demo/cars').then(
            response=>{
              console.log("请求成功了",response.data)
            },
            error=>{
              console.log('请求失败了',error.message)
            }
          )
        }
      }
    }
</script>

3、插槽

1.作用:

让父组件可以向子组件指定位置插入html结构,也是一种组件间通信的方式 适用于 父组件 ===>子组件

2.分类:

默认插槽、具名插槽、作用域插槽

3.使用方式:

1、默认插槽
父组件中:
	<Category>
		<div>html结构1</div>
	</Category>
子组件中:
	<template>
		<div>
			<!-- 定义插槽 -->
			<slot>插槽默认内容...</slot>
		</div>
	</template>
2、具名插槽
父组件中
	<Category>
		<template slot="center"
			<div>htm1结构1</div>
		</template>
		<template v-slot:footer>
			<div>html结构2</div>
		</template>
	</Category>
子组件中:
	<template>
		<div>
			<!--定义插槽 -->
			<slot name="center">插槽默认内容...</slot>
			<slot name="footer">插槽默认内容...</slot>
		</div>
	</template>

3.作用域插槽:

1.理解:

数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定。 (games数据在Category组件中,但使用数据所遍历出来的结构由App组件决定)

2.具体编码
父组件中:
	<Category>
		<template scope="scopeData">
			<!-- 生成的是u1列表 -->
			<ul>
				<li v-for="g in scopeData.games” :key="g">((g)}</1i>
			</u1>
		</template>
	</Category>
	<Category>
		<template slot-scope="scopeData">
			<!-- 生成的是h4标题 -->
			<h4 v-for="g in scopeData.games” :key="g">(g]]</h4>
		</template>
	</Category>
子组件中,
	<template>
		<div>
			<slot :games="games"></slot>
		</div>
	</template>
	<script>
		export default {
			name:'Category",
			props:['title'],
			//数据在子组件自身
			data(){
				return {
					games:['红色警戒”,穿越火线,劲舞团,超级玛丽]
				},
	</script>

第 5 章:vuex

1、理解 vuex

1 vuex 是什么

  1. 概念:专门在 Vue 中实现集中式状态(数据)管理的一个Vue 插件,对vue应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,且适用于任意组件间通信。
  2. Github 地址: https://github.com/vuejs/vuex

2、什么时候使用 Vuex

1.多个组件依赖于同一状态(数据)
2.来自不同组件的行为需要变更同一状态(数据)

3、搭建环境

1、创建文件:src/store/index.js

//该文件用于创建vuex中最为核心的store

// 引入vue核心库
import Vue from 'vue'
//引入vuex
import Vuex from 'vuex'
// 应用Vuex插件
Vuex.usr(Vuex)  

//准备action-用于响应组件中的动作
const actions={}
//准备mutations-用于操作数据(state)
const mutations={}
//准备state-用于存储数据
const state ={}

// 创建并暴露store
export default new Vuex.Store({
    actions,
    mutations,
    state
})

2、在mian.js中创建vm时传入store配置项

//引入Vue
import Vue from 'vue'
//引入App
import App from './App.vue'
//关闭Vue的生产提示
Vue.config.productionTip = false
//引入插件
import vueResource from 'vue-resource'

// 引入store
import store from './store/index'

// 使用插件
Vue.use(vueResource)

//创建vm
new Vue({
	el:'#app',
	render: h => h(App),
	store
})

4、基本使用

1、初始化数据、配置actions、配置mutations,操作文件store.js

//该文件用于创建vuex中最为核心的store

// 引入vue核心库
import Vue from 'vue'
//引入vuex
import Vuex from 'vuex'
// 应用Vuex插件
Vuex.usr(Vuex)  

//准备action-用于响应组件中的动作
const actions={
    // jia(miniStore,value){
    //     miniStore.commit('JIA',value)
    // },
    // jian(miniStore,value){
    //     miniStore.commit('JIAN',value)
    // },
    jiaOdd(miniStore,value){
        if(miniStore.state.sum % 2){
            miniStore.commit('JIA',value)
        }
    },
    jiaWait(miniStore,value){
        setTimeout(() => {
            miniStore.commit('JIA',value)
        }, 500);
    },
}
//准备mutations-用于操作数据(state)
const mutations={
    JIA(state,value){
        state.sum+=value
    },
    JIAN(state,value){
        state.sum-=value
    }
}
//准备state-用于存储数据
const state ={
    sum:0,//当前和
}

// 创建并暴露store
export default new Vuex.Store({
    actions,
    mutations,
    state
})

2、组件中读取vuex中的数据

$store.state.sum

3.组件中修改vuex中的数据:

 $store.dispatch('action中的方法名,数据) 或 $store.commit('mutations中的方法名',数据)

备注: 若没有网络请求或其他业务逻辑,组件中也可以越过actions,即不写dispath,直接编写commit
如:

<template>
    <div>
        <h1>当前求和为:{{$store.state.sum}}</h1>
        <select v-model='n'>
            <option :value="1">1</option>
            <option :value="2">2</option>
            <option :value="3">3</option>
        </select>
        <button @click='increment'>+</button>
        <button @click='decrement'>-</button>
        <button @click='incrementOdd'>当前求和为奇数时加</button>
        <button @click='incrementWait'>等一等再加</button>
    </div>
</template>
<script>
export default {
    name:'Category',
    date(){
        return{
            // sum:0,//当前和
            n:1,//用户选择的数字
        }
    },
    methods:{
        increment(){
            // this.sum+this.n
            //this.$store.dispatch('jia',this.n)
            //没有业务逻辑可以dispatch 直接写成
            this.$store.commit('JIA',n)
        },
        decrement(){
            // this.sum-=this.n
            this.$store.dispatch('jian',this.n)
        },
        incrementOdd(){
            // if(this.sum % 2){
            //     this.sum+=this.n
            // }
             this.$store.dispatch('jiaOdd',this.n)
        },
        incrementWait(){
            // setTimeout(() => {
            //     this.sum+this.n
            // }, 500);
             this.$store.dispatch('jiaWait',this.n)
        },
    }
}
</script>

<style>

</style>

5、getters的使用

1.概念:

当state中的数据需要经过加工后再使用时,可以使用getters加工。

2.在store.js 中追加getters 配置

	......
	const getters = {
		bigSum(state){
			return state.sum * 10
		}
	}
	//创建并暴露store
	export default new Vuex.Store({
		......
		getters
	})

3.组件中读取数据:

$store.getters.bigSum

如:

<h3>当前求和放大十倍后为:{{$store.getters.bigSum}}</h3>

6、四个map方法的使用

1.mapstate方法:

用于帮助我们映射state 中的数据为计算属性

computed:{
	//借助mapState生成计算属性: sum、schoo1、subject (对象写法)
	...mapState((sum:'sum",school: 'school",subject:'subject"}),
	//借助mapstate生成计算属性: sum、schoo1、subject (数组写法)
	...mapState(["sum',"school',"subject"]),
}

2.mapGetters方法:

用于帮助我们映射getters 中的数据为计算属性

computed:{
	//借助mapGetters生成计算属性。bigSum (对象写法)
	...mapGetters({bigSum;"bigSum"}).
	//借助mapGetters生成计算属性: bigSum (数组写法)
	...mapGetters(['bigSum"])
}

3.mapActions方法:

用于帮助我们生成与actions 对话的方法,即: 包含$store.dispatch(xxx)的函数

	methods:{
		//靠mapActions生成:incrementOdd、incrementWait (对象形式)
		..mapActions(fincrementOdd:'jiaOdd',incrementWait:"jiaWait'})
		//靠mapActions生成:incrementOdd、incrementWait (数组形式)
		...mapActions(["jiaOdd","jiawait"])
	}

4.mapMutations方法:

用于帮助我们生成与mutations对话的方法,即:包含$store,commit(xxx)的函数

methods:(
	//靠mapActions生成: increment、decrement (对象形式)
	...mapMutations({increment:'JIA',decrement:'JIAN'}),
	//靠mapMutations生成:JIA、JIAN (对象形式)
	...mapMutations(['JIA',JIAN']),
}

备注: mapActions与mapMutations使用时,若需要传递参数需要: 在模板中绑定事件时传递好参数,否则参数是事件对象。
如:

<template>
	<div>
		<h1>当前求和为:{{sum}}</h1>
		<h3>当前求和放大10倍为:{{bigSum}}</h3>
		<h3>我在{{school}},学习{{subject}}</h3>
		<select v-model.number="n">
			<option value="1">1</option>
			<option value="2">2</option>
			<option value="3">3</option>
		</select>
		<button @click="increment(n)">+</button>
		<button @click="decrement(n)">-</button>
		<button @click="incrementOdd(n)">当前求和为奇数再加</button>
		<button @click="incrementWait(n)">等一等再加</button>
	</div>
</template>

<script>
	import {mapState,mapGetters,mapMutations,mapActions} from 'vuex'
	export default {
		name:'Count',
		data() {
			return {
				n:1, //用户选择的数字
			}
		},
		computed:{
			//靠程序员自己亲自去写计算属性
			/* sum(){
				return this.$store.state.sum
			},
			school(){
				return this.$store.state.school
			},
			subject(){
				return this.$store.state.subject
			}, */

			//借助mapState生成计算属性,从state中读取数据。(对象写法)
			// ...mapState({he:'sum',xuexiao:'school',xueke:'subject'}),

			//借助mapState生成计算属性,从state中读取数据。(数组写法)
			...mapState(['sum','school','subject']),

			/* ******************************************************************** */

			/* bigSum(){
				return this.$store.getters.bigSum
			}, */

			//借助mapGetters生成计算属性,从getters中读取数据。(对象写法)
			// ...mapGetters({bigSum:'bigSum'})
			
			//借助mapGetters生成计算属性,从getters中读取数据。(数组写法)
			...mapGetters(['bigSum'])

		},
		methods: {
			//程序员亲自写方法
			/* increment(){
				this.$store.commit('JIA',this.n)
			},
			decrement(){
				this.$store.commit('JIAN',this.n)
			}, */

			//借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(对象写法)
			...mapMutations({increment:'JIA',decrement:'JIAN'}),

			//借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(数组写法)
			// ...mapMutations(['JIA','JIAN']),

			/* ************************************************* */

			//程序员亲自写方法
			/* incrementOdd(){
				this.$store.dispatch('jiaOdd',this.n)
			},
			incrementWait(){
				this.$store.dispatch('jiaWait',this.n)
			}, */

			//借助mapActions生成对应的方法,方法中会调用dispatch去联系actions(对象写法)
			...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'})

			//借助mapActions生成对应的方法,方法中会调用dispatch去联系actions(数组写法)
			// ...mapActions(['jiaOdd','jiaWait'])
		},
		mounted() {
			const x = mapState({he:'sum',xuexiao:'school',xueke:'subject'})
			console.log(x)
		},
	}
</script>

<style lang="css">
	button{
		margin-left: 5px;
	}
</style>

7.模块化+命名空间

1.目的:

让代码更好维护,让多种数据分类更加明确

2.修改store.js

const countAbout = {
	namespaced;true,//开启命名空间state:(x:1),
	mutations:(...}
	actions:[..},
	getters: {
		bigSum(state){
			return state.sum * 10
		}
	}
}


const personAbout = {
	namespaced;true,//开启命名空间
	state:{ ... },
	mutations: { ...},
	actions:{ ...}
}
const store = new Vuex.Store(
	modules:{
		countAbout.
		personAbout
	}
})

在这里插入图片描述
如:

第 6 章:vue-router路由

1 vue-router 的理解

vue 的一个插件库,专门用来实现 SPA 应用

2 对 SPA 应用的理解

  1. 单页 Web 应用(single page web application,SPA)。
  2. 整个应用只有一个完整的页面。
  3. 点击页面中的导航链接不会刷新页面,只会做页面的局部更新。
  4. 数据需要通过 ajax 请求获取。

3 路由的理解

1. 什么是路由?

  1. 一个路由就是一组映射关系(key - value)
  2. key 为路径, value 可能是 function 或 component

2. 路由分类

1. 后端路由:
  1. 理解:value 是 function, 用于处理客户端提交的请求。
  2. 工作过程:服务器接收到一个请求时, 根据请求路径找到匹配的函数来处理请求, 返回响应数据。
2. 前端路由:
  1. 理解:value 是 component,用于展示页面内容。
  2. 工作过程:当浏览器的路径改变时, 对应的组件就会显示。

4 路由的基本使用

1.安装vue-router,命令:

npm i vue-router@3

2.应用插件:

Vue.use(VueRouter)

如:

//引入Vue
import Vue from 'vue'
//引入App
import App from './App.vue'
//关闭Vue的生产提示
Vue.config.productionTip = false
//引入vueRouter
import VueRouter from 'vue-router'
//引入路由器
import router from './router/index'

//应用插件
Vue.use(VueRouter)


//创建vm
new Vue({
	el:'#app',
	render: h => h(App),
	router:router
})

3.编写router配置项:

//该文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router'
//引入组件
import About from '@/components/About'
import Home from '../components/Home'

//创建并暴露路由器,去管理一组一组的路由规则
 export default new VueRouter({
    routes:[
        {
            path:'/about',
            component:About
        },
        {
            path:'/home',
            component:Home
        }
    ]
})

4.实现切换(active-class可配置高亮样式)

<router-link active-class="active" to="/about">About</router-link>

5.指定展示位置

<router-view></router-view>

如:

<template>
	 <div>
    <div class="row">
      <div class="col-xs-offset-2 col-xs-8">
        <div class="page-header"><h2>Vue Router Demo</h2></div>
      </div>
    </div>
    <div class="row">
      <div class="col-xs-2 col-xs-offset-2">
        <div class="list-group">
			<!-- 原始html中我们使用a标签实现页面的跳转 -->
         <!-- <a class="list-group-item active" href="./about.html">About</a>
          <a class="list-group-item" href="./home.html">Home</a> -->
			<!--Vue中借助router-lick标签实现路由的切换  -->
		  <router-link class="list-group-item " active-class="active" to="/about">About</router-link>
          <router-link class="list-group-item" active-class="active" to="/home">Home</router-link>
		</div>
      </div>
      <div class="col-xs-6">
        <div class="panel">
          <div class="panel-body">
			<!-- 指定组件的呈现位置 -->
            <router-view></router-view>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
	
	export default {
		name:'App'
	}
</script>
<style scoped>

</style>

6.几个注意点

1.路由组件通常存放在pages 文件夹,一般组件通常存放在components 文件夹
2.通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载。
3.每个组件都有自己的 $route 属性,里面存储着自己的路由信息
4.整个应用只有一个router,可以通过组件的 $router 属性获取到。

5、嵌套(多级)路由

1.配置路由规则,使用children配置项:

routes:[
	{
		path:'/about"
		component :About .
	},
	{
		path : '/home",
		component :Home,
		children:{//通过children配置子级路由
			{
			 	 path:'news'//此处一定不要写: /news
			  	 component:News
			}, 
			{
				 path:'message’,//此处一定不要写,/message
				 component:Message
			}
		}
	}	
]

2.跳转 (要写完整路径) :

<router-link to="/home/news">News</router-link>

6、路由的query传参

1、传递参数

<ul>
	<li v-for="m in messageList" :key="m.id">
		<!-- 跳转路由并携带query参数,to的字符串写法 -->
		<!-- <router-link :to="`/home/message/detail?id=${m.id}&title=${m.title}`">{{m.title}}</router-link>&nbsp;&nbsp; -->

		<!-- 跳转路由并携带query参数,to的对象写法 -->
		<router-link :to="{
			path:'/home/message/detail',
			query:{
				id:m.id,
				title:m.title
			}
		}">
		</router-link>
	
	</li>
</ul>

2、接收参数

{{$route.query.id}}
{{$route.query.title}}

7、命名路

1.作用:

可以简化路由的跳转

2.如何使用

1.给路由命名

在这里插入图片描述

2.简化跳转:

在这里插入图片描述

8、路由的params参数

1、配置路由,声明接收 params参数

在这里插入图片描述

2、传递参数

在这里插入图片描述

3、接收参数

在这里插入图片描述

9.路由的props配置

作用: 让路由组件更方便的收到参数

{
	name:'xiangqing',
	path:'detail/:id',
	component:Detail,

	//第一种写法:props值为对象,该对象中所有的key-value的组合最终都会通过props传给Detail组件
	// props:{a:900}

	//第二种写法:props值为布尔值,布尔值为true,则把路由收到的所有params参数通过props传给Detail组件
	// props:true
	
	//第三种写法:props值为函数,该函数返回的对象中每一组key-value都会通过props传给Detail组件
	props(route){
		return {
			id:route.query.id,
			title:route.query.title
		}
	}
}

接收

<template>
	<ul>
		<li>消息编号:{{id}}</li>
		<li>消息标题:{{title}}</li>
	</ul>
</template>

<script>
	export default {
		name:'Detail',
		props:['id','title'],
		
	}
</script>

10.<router-link>的replace属性

  1. 作用:控制路由跳转时操作浏览器历史记录的模式
  2. 浏览器的历史记录有两种写入方式:分别为pushreplacepush是追加历史记录,replace是替换当前记录。路由跳转时候默认为push
  3. 如何开启replace模式:<router-link replace .......>News</router-link>

11.编程式路由导航

  1. 作用:不借助<router-link> 实现路由跳转,让路由跳转更加灵活

  2. 具体编码:

    //$router的两个API
    this.$router.push({
    	name:'xiangqing',
    		params:{
    			id:xxx,
    			title:xxx
    		}
    })
    
    this.$router.replace({
    	name:'xiangqing',
    		params:{
    			id:xxx,
    			title:xxx
    		}
    })
    this.$router.forward() //前进
    this.$router.back() //后退
    this.$router.go() //可前进也可后退
    

如:

<template>
	<div>
		<ul>
			<li v-for="m in messageList" :key="m.id">
				<!-- 跳转路由并携带params参数,to的字符串写法 -->
				<!-- <router-link :to="`/home/message/detail/${m.id}/${m.title}`">{{m.title}}</router-link>&nbsp;&nbsp; -->

				<!-- 跳转路由并携带params参数,to的对象写法 -->
				<router-link :to="{
					name:'xiangqing',
					query:{
						id:m.id,
						title:m.title
					}
				}">
					{{m.title}}
				</router-link>
				<button @click="pushShow(m)">push查看</button>
				<button @click="replaceShow(m)">replace查看</button>
			</li>
		</ul>
		<hr>
		<router-view></router-view>
	</div>
</template>

<script>
	export default {
		name:'Message',
		data() {
			return {
				messageList:[
					{id:'001',title:'消息001'},
					{id:'002',title:'消息002'},
					{id:'003',title:'消息003'}
				]
			}
		},
		methods: {
			pushShow(m){
				this.$router.push({
					name:'xiangqing',
					query:{
						id:m.id,
						title:m.title
					}
				})
			},
			replaceShow(m){
				this.$router.replace({
					name:'xiangqing',
					query:{
						id:m.id,
						title:m.title
					}
				})
			}
		},
	}
</script>
<template>
	<div class="col-xs-offset-2 col-xs-8">
		<div class="page-header">
			<h2>Vue Router Demo</h2>
			<button @click="back">后退</button>
			<button @click="forward">前进</button>
			<button @click="test">测试一下go</button>
		</div>
	</div>
</template>

<script>
	export default {
		name:'Banner',
		methods: {
			back(){
				this.$router.back()
				// console.log(this.$router)
			},
			forward(){
				this.$router.forward()
			},
			test(){
				this.$router.go(3)
			}
		},
	}
</script>

11、缓存路由组件

  1. 作用:让不展示的路由组件保持挂载,不被销毁。

  2. 具体编码:

   	 <!-- 缓存多个路由组件 -->
   	 <!-- <keep-alive :include="['News','Message']">
   		    <router-view></router-view>
      </keep-alive>
   -->
   	
 	  <!-- 缓存一个路由组件 -->
      <keep-alive include="不被销毁的组件名"> 
          <router-view></router-view>
      </keep-alive>

12.两个新的生命周期钩子

  1. 作用:路由组件所独有的两个钩子,用于捕获路由组件的激活状态。
  2. 具体名字:
    1. activated路由组件被激活时触发。
    2. deactivated路由组件失活时触发。
<template>
	<ul>
		<li :style="{opacity}">欢迎学习Vue</li>
		<li>news001 <input type="text"></li>
		<li>news002 <input type="text"></li>
		<li>news003 <input type="text"></li>
	</ul>
</template>

<script>
	export default {
		name:'News',
		data() {
			return {
				opacity:1
			}
		},
		//beforeDestroy和mounted对路由不起作用
		/* beforeDestroy() {
			console.log('News组件即将被销毁了')
			clearInterval(this.timer)
		}, */
		/* mounted(){
			this.timer = setInterval(() => {
				console.log('@')
				this.opacity -= 0.01
				if(this.opacity <= 0) this.opacity = 1
			},16)
		}, */
		activated() {
			console.log('News组件被激活了')
			this.timer = setInterval(() => {
				console.log('@')
				this.opacity -= 0.01
				if(this.opacity <= 0) this.opacity = 1
			},16)
		},
		deactivated() {
			console.log('News组件失活了')
			clearInterval(this.timer)
		},
	}
</script>

13、路由守卫

1. 作用:

对路由进行权限控制

2. 分类:

全局守卫、独享守卫、组件内守卫

3. 全局守卫:

//全局前置守卫:初始化时执行、每次路由切换前执行
router.beforeEach((to,from,next)=>{
	console.log('beforeEach',to,from)
	if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制
		if(localStorage.getItem('school') === 'atguigu'){ //权限控制的具体规则
			next() //放行
		}else{
			alert('暂无权限查看')
			// next({name:'guanyu'})
		}
	}else{
		next() //放行
	}
})

//全局后置守卫:初始化时执行、每次路由切换后执行
router.afterEach((to,from)=>{
	console.log('afterEach',to,from)
	if(to.meta.title){ 
		document.title = to.meta.title //修改网页的title
	}else{
		document.title = 'vue_test'
	}
})

如:

// 该文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router'
//引入组件
import About from '../pages/About'
import Home from '../pages/Home'
import News from '../pages/News'
import Message from '../pages/Message'
import Detail from '../pages/Detail'

//创建并暴露一个路由器
const router =  new VueRouter({
	routes:[
		{
			name:'guanyu',
			path:'/about',
			component:About,
			meta:{title:'关于'}
		},
		{
			name:'zhuye',
			path:'/home',
			component:Home,
			meta:{title:'主页'},
			children:[
				{
					name:'xinwen',
					path:'news',
					component:News,
					meta:{isAuth:true,title:'新闻'}
				},
				{
					name:'xiaoxi',
					path:'message',
					component:Message,
					meta:{isAuth:true,title:'消息'},
					children:[
						{
							name:'xiangqing',
							path:'detail',
							component:Detail,
							meta:{isAuth:true,title:'详情'},
						}
					]
				}
			]
		}
	]
})

//全局前置路由守卫————初始化的时候被调用、每次路由切换之前被调用
router.beforeEach((to,from,next)=>{
	console.log('前置路由守卫',to,from)
	if(to.meta.isAuth){ //判断是否需要鉴权
		if(localStorage.getItem('school')==='atguigu'){
			next()
		}else{
			alert('学校名不对,无权限查看!')
		}
	}else{
		next()
	}
})

//全局后置路由守卫————初始化的时候被调用、每次路由切换之后被调用
router.afterEach((to,from)=>{
	console.log('后置路由守卫',to,from)
	document.title = to.meta.title || '硅谷系统'
})

export default router

4. 独享守卫:

beforeEnter(to,from,next){
	console.log('beforeEnter',to,from)
	if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制
		if(localStorage.getItem('school') === 'atguigu'){
			next()
		}else{
			alert('暂无权限查看')
			// next({name:'guanyu'})
		}
	}else{
		next()
	}
}

如:

// 该文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router'
//引入组件
import About from '../pages/About'
import Home from '../pages/Home'
import News from '../pages/News'
import Message from '../pages/Message'
import Detail from '../pages/Detail'

//创建并暴露一个路由器
const router =  new VueRouter({
	routes:[
		{
			name:'guanyu',
			path:'/about',
			component:About,
			meta:{title:'关于'}
		},
		{
			name:'zhuye',
			path:'/home',
			component:Home,
			meta:{title:'主页'},
			children:[
				{
					name:'xinwen',
					path:'news',
					component:News,
					meta:{isAuth:true,title:'新闻'},
					beforeEnter: (to, from, next) => {
						console.log('独享路由守卫',to,from)
						if(to.meta.isAuth){ //判断是否需要鉴权
							if(localStorage.getItem('school')==='atguigu'){
								next()
							}else{
								alert('学校名不对,无权限查看!')
							}
						}else{
							next()
						}
					}
				},
				{
					name:'xiaoxi',
					path:'message',
					component:Message,
					meta:{isAuth:true,title:'消息'},
					children:[
						{
							name:'xiangqing',
							path:'detail',
							component:Detail,
							meta:{isAuth:true,title:'详情'},
						}
					]
				}
			]
		}
	]
})
export default router

5. 组件内守卫:

//进入守卫:通过路由规则,进入该组件时被调用
beforeRouteEnter (to, from, next) {
},
//离开守卫:通过路由规则,离开该组件时被调用
beforeRouteLeave (to, from, next) {
}

如:

<template>
	<h2>我是About的内容</h2>
</template>

<script>
	export default {
		name:'About',
		//通过路由规则,进入该组件时被调用
		beforeRouteEnter (to, from, next) {
			console.log('About--beforeRouteEnter',to,from)
			if(to.meta.isAuth){ //判断是否需要鉴权
				if(localStorage.getItem('school')==='atguigu'){
					next()
				}else{
					alert('学校名不对,无权限查看!')
				}
			}else{
				next()
			}
		},

		//通过路由规则,离开该组件时被调用
		beforeRouteLeave (to, from, next) {
			console.log('About--beforeRouteLeave',to,from)
			next()
		}
	}
</script>

14、路由器的两种工作模式

  1. 对于一个url来说,什么是hash值?—— #及其后面的内容就是hash值。
  2. hash值不会包含在 HTTP 请求中,即:hash值不会带给服务器。
  3. hash模式:
    1. 地址中永远带着#号,不美观 。
    2. 若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法。
    3. 兼容性较好。
  4. history模式:
    1. 地址干净,美观 。
    2. 兼容性和hash模式相比略差。
    3. 应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题。

如:

// 该文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router'
//引入组件
import About from '../pages/About'
import Home from '../pages/Home'
import News from '../pages/News'
import Message from '../pages/Message'
import Detail from '../pages/Detail'

//创建并暴露一个路由器
const router =  new VueRouter({
	mode:'history',//选择路由模式
	routes:[
		
	]
})

export default router

vue3快速上手

一、创建Vue3.0工程

1.使用 vue-cli 创建

官方文档:https://cli.vuejs.org/zh/guide/creating-a-project.html#vue-create

## 查看@vue/cli版本,确保@vue/cli版本在4.5.0以上
vue --version
## 安装或者升级你的@vue/cli
npm install -g @vue/cli
## 创建
vue create vue_test
## 启动
cd vue_test
npm run serve

2.使用 vite 创建

官方文档:https://v3.cn.vuejs.org/guide/installation.html#vite

vite官网:https://vitejs.cn

  • 什么是vite?—— 新一代前端构建工具。
  • 优势如下:
    • 开发环境中,无需打包操作,可快速的冷启动。
    • 轻量快速的热重载(HMR)。
    • 真正的按需编译,不再等待整个应用编译完成。
  • 传统构建 与 vite构建对比图
## 创建工程
npm init vite-app <project-name>
## 进入工程目录
cd <project-name>
## 安装依赖
npm install
## 运行
npm run dev

3.分析工程结构

main.js

//引入的不再是Vue构造函数了,引入的是一个名为createApp的工厂函数
import { createApp } from 'vue'
import App from './App.vue'

//创建应用实例对象——app(类似于之前Vue2中的vm,但app比vm更“轻”)
const app = createApp(App)

//挂载
app.mount('#app')

二、常用 Composition API

官方文档: https://v3.cn.vuejs.org/guide/composition-api-introduction.html

1.拉开序幕的setup

  1. 理解:Vue3.0中一个新的配置项,值为一个函数。
  2. setup是所有Composition API(组合API)“ 表演的舞台 ”
  3. 组件中所用到的:数据、方法等等,均要配置在setup中。
  4. setup函数的两种返回值:
    1. 若返回一个对象,则对象中的属性、方法, 在模板中均可以直接使用。(重点关注!)
    2. 若返回一个渲染函数:则可以自定义渲染内容。(了解)
  5. 注意点:
    1. 尽量不要与Vue2.x配置混用
      • Vue2.x配置(data、methos、computed…)中可以访问到setup中的属性、方法。
      • 但在setup中不能访问到Vue2.x配置(data、methos、computed…)。
      • 如果有重名, setup优先。
    2. setup不能是一个async函数,因为返回值不再是return的对象, 而是promise, 模板看不到return对象中的属性。(后期也可以返回一个Promise实例,但需要Suspense和异步组件的配合)

2.ref函数

  • 作用: 定义一个响应式的数据
  • 语法: const xxx = ref(initValue)
    • 创建一个包含响应式数据的引用对象(reference对象,简称ref对象)
    • JS中操作数据: xxx.value
    • 模板中读取数据: 不需要.value,直接:<div>{{xxx}}</div>
  • 备注:
    • 接收的数据可以是:基本类型、也可以是对象类型。
    • 基本类型的数据:响应式依然是靠Object.defineProperty()getset完成的。
    • 对象类型的数据:内部 “ 求助 ” 了Vue3.0中的一个新函数—— reactive函数。

如:

<template>
	<h1>一个人的信息</h1>
	<h2>姓名:{{name}}</h2>
	<h2>年龄:{{age}}</h2>
	<h3>工作种类:{{job.type}}</h3>
	<h3>工作薪水:{{job.salary}}</h3>
	<button @click="changeInfo">修改人的信息</button>
</template>

<script>
	import {ref} from 'vue'
	export default {
		name: 'App',
		setup(){
			//数据
			let name = ref('张三')
			let age = ref(18)
			let job = ref({
				type:'前端工程师',
				salary:'30K'
			})

			//修改方法
			function changeInfo(){
				name.value = '李四'
				age.value = 48
				console.log(job.value)
				job.value.type = 'UI设计师'
				job.value.salary = '60K'
				console.log(name,age)
			}

			//返回一个对象(常用)
			return {
				name,
				age,
				job,
				changeInfo
			}
		}
	}
</script>

3.reactive函数

  • 作用: 定义一个对象类型的响应式数据(基本类型不要用它,要用ref函数)
  • 语法:const 代理对象= reactive(源对象)接收一个对象(或数组),返回一个代理对象(Proxy的实例对象,简称proxy对象)
  • reactive定义的响应式数据是“深层次的”。
  • 内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据进行操作。
    如:
<template>
	<h1>一个人的信息</h1>
	<h2>姓名:{{person.name}}</h2>
	<h2>年龄:{{person.age}}</h2>
	<h3>工作种类:{{person.job.type}}</h3>
	<h3>工作薪水:{{person.job.salary}}</h3>
	<h3>爱好:{{person.hobby}}</h3>
	<h3>测试的数据c:{{person.job.a.b.c}}</h3>
	<button @click="changeInfo">修改人的信息</button>
</template>

<script>
	import {reactive} from 'vue'
	export default {
		name: 'App',
		setup(){
			//数据
			let person = reactive({
				name:'张三',
				age:18,
				job:{
					type:'前端工程师',
					salary:'30K',
					a:{
						b:{
							c:666
						}
					}
				},
				hobby:['抽烟','喝酒','烫头']
			})

			//方法
			function changeInfo(){
				person.name = '李四'
				person.age = 48
				person.job.type = 'UI设计师'
				person.job.salary = '60K'
				person.job.a.b.c = 999
				person.hobby[0] = '学习'
			}

			//返回一个对象(常用)
			return {
				person,
				changeInfo
			}
		}
	}
</script>

4.Vue3.0中的响应式原理

vue2.x的响应式

  • 实现原理:

    • 对象类型:通过Object.defineProperty()对属性的读取、修改进行拦截(数据劫持)。

    • 数组类型:通过重写更新数组的一系列方法来实现拦截。(对数组的变更方法进行了包裹)。

      Object.defineProperty(data, 'count', {
          get () {}, 
          set () {}
      })
      
  • 存在问题:

    • 新增属性、删除属性, 界面不会更新。
    • 直接通过下标修改数组, 界面不会自动更新。

Vue3.0的响应式

  • 实现原理:

    • 通过Proxy(代理): 拦截对象中任意属性的变化, 包括:属性值的读写、属性的添加、属性的删除等。

    • 通过Reflect(反射): 对源对象的属性进行操作。

    • MDN文档中描述的Proxy与Reflect:

      • Proxy:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy

      • Reflect:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect

        new Proxy(data, {
        	// 拦截读取属性值
            get (target, prop) {
            	return Reflect.get(target, prop)
            },
            // 拦截设置属性值或添加新属性
            set (target, prop, value) {
            	return Reflect.set(target, prop, value)
            },
            // 拦截删除属性
            deleteProperty (target, prop) {
            	return Reflect.deleteProperty(target, prop)
            }
        })
        
        proxy.name = 'tom'   
        

5.reactive对比ref

  • 从定义数据角度对比:
    • ref用来定义:基本类型数据
    • reactive用来定义:对象(或数组)类型数据
    • 备注:ref也可以用来定义对象(或数组)类型数据, 它内部会自动通过reactive转为代理对象
  • 从原理角度对比:
    • ref通过Object.defineProperty()getset来实现响应式(数据劫持)。
    • reactive通过使用Proxy来实现响应式(数据劫持), 并通过Reflect操作源对象内部的数据。
  • 从使用角度对比:
    • ref定义的数据:操作数据需要.value,读取数据时模板中直接读取不需要.value
    • reactive定义的数据:操作数据与读取数据:均不需要.value

6.setup的两个注意点

  • setup执行的时机

    • 在beforeCreate之前执行一次,this是undefined。
  • setup的参数

    • props:值为对象,包含:组件外部传递过来,且组件内部声明接收了的属性。
    • context:上下文对象
      • attrs: 值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性, 相当于 this.$attrs
      • slots: 收到的插槽内容, 相当于 this.$slots
      • emit: 分发自定义事件的函数, 相当于 this.$emit

7.计算属性与监视

1.computed函数

  • 与Vue2.x中computed配置功能一致

  • 写法

    import {computed} from 'vue'
    
    setup(){
     //数据
      	let person = reactive({
      		firstName:'张',
      		lastName:'三'
      	})
      	//计算属性——简写(没有考虑计算属性被修改的情况)
      	/* person.fullName = computed(()=>{
      		return person.firstName + '-' + person.lastName
      	}) */
    	//计算属性——简写
        let fullName = computed(()=>{
            return person.firstName + '-' + person.lastName
        })
        //计算属性——完整
        let fullName = computed({
            get(){
                return person.firstName + '-' + person.lastName
            },
            set(value){
                const nameArr = value.split('-')
                person.firstName = nameArr[0]
                person.lastName = nameArr[1]
            }
        })
    }
    

如:

<template>
	<h1>一个人的信息</h1>
	姓:<input type="text" v-model="person.firstName">
	<br>
	名:<input type="text" v-model="person.lastName">
	<br>
	<span>全名:{{person.fullName}}</span>
	<br>
	全名:<input type="text" v-model="person.fullName">
</template>

<script>
	import {reactive,computed} from 'vue'
	export default {
		name: 'Demo',
		setup(){
			//数据
			let person = reactive({
				firstName:'张',
				lastName:'三'
			})
			//计算属性——简写(没有考虑计算属性被修改的情况)
			/* person.fullName = computed(()=>{
				return person.firstName + '-' + person.lastName
			}) */

			//计算属性——完整写法(考虑读和写)
			person.fullName = computed({
				get(){
					return person.firstName + '-' + person.lastName
				},
				set(value){
					const nameArr = value.split('-')
					person.firstName = nameArr[0]
					person.lastName = nameArr[1]
				}
			})

			//返回一个对象(常用)
			return {
				person
			}
		}
	}
</script>


2.watch函数

  • 与Vue2.x中watch配置功能一致

  • 两个小“坑”:

    • 监视reactive定义的响应式数据时:oldValue无法正确获取、强制开启了深度监视(deep配置失效)。
    • 监视reactive定义的响应式数据中某个属性时:deep配置有效。
    //情况一:监视ref定义的响应式数据
    watch(sum,(newValue,oldValue)=>{
    	console.log('sum变化了',newValue,oldValue)
    },{immediate:true})
    
    //情况二:监视多个ref定义的响应式数据
    watch([sum,msg],(newValue,oldValue)=>{
    	console.log('sum或msg变化了',newValue,oldValue)
    }) 
    
    /* 情况三:监视reactive定义的响应式数据
    			若watch监视的是reactive定义的响应式数据,则无法正确获得oldValue!!
    			若watch监视的是reactive定义的响应式数据,则强制开启了深度监视 
    */
    watch(person,(newValue,oldValue)=>{
    	console.log('person变化了',newValue,oldValue)
    },{immediate:true,deep:false}) //此处的deep配置不再奏效
    
    //情况四:监视reactive定义的响应式数据中的某个属性
    watch(()=>person.job,(newValue,oldValue)=>{
    	console.log('person的job变化了',newValue,oldValue)
    },{immediate:true,deep:true}) 
    
    //情况五:监视reactive定义的响应式数据中的某些属性
    watch([()=>person.job,()=>person.name],(newValue,oldValue)=>{
    	console.log('person的job变化了',newValue,oldValue)
    },{immediate:true,deep:true})
    
    //特殊情况
    watch(()=>person.job,(newValue,oldValue)=>{
        console.log('person的job变化了',newValue,oldValue)
    },{deep:true}) //此处由于监视的是reactive素定义的对象中的某个属性,所以deep配置有效
    

如:

<template>
	<h2>当前求和为:{{sum}}</h2>
	<button @click="sum++">点我+1</button>
	<hr>
	<h2>当前的信息为:{{msg}}</h2>
	<button @click="msg+='!'">修改信息</button>
	<hr>
	<h2>姓名:{{person.name}}</h2>
	<h2>年龄:{{person.age}}</h2>
	<h2>薪资:{{person.job.j1.salary}}K</h2>
	<button @click="person.name+='~'">修改姓名</button>
	<button @click="person.age++">增长年龄</button>
	<button @click="person.job.j1.salary++">涨薪</button>
</template>

<script>
	import {ref,reactive,watch} from 'vue'
	export default {
		name: 'Demo',
		setup(){
			//数据
			let sum = ref(0)
			let msg = ref('你好啊')
			let person = reactive({
				name:'张三',
				age:18,
				job:{
					j1:{
						salary:20
					}
				}
			})

			//情况一:监视ref所定义的一个响应式数据
			/* watch(sum,(newValue,oldValue)=>{
				console.log('sum变了',newValue,oldValue)
			},{immediate:true}) */

			//情况二:监视ref所定义的多个响应式数据
			/* watch([sum,msg],(newValue,oldValue)=>{
				console.log('sum或msg变了',newValue,oldValue)
			},{immediate:true}) */

			/* 
				情况三:监视reactive所定义的一个响应式数据的全部属性
						1.注意:此处无法正确的获取oldValue
						2.注意:强制开启了深度监视(deep配置无效)
			*/
			/* watch(person,(newValue,oldValue)=>{
				console.log('person变化了',newValue,oldValue)
			},{deep:false}) //此处的deep配置无效 */

			//情况四:监视reactive所定义的一个响应式数据中的某个属性
			/* watch(()=>person.name,(newValue,oldValue)=>{
				console.log('person的name变化了',newValue,oldValue)
			})  */

			//情况五:监视reactive所定义的一个响应式数据中的某些属性
			/* watch([()=>person.name,()=>person.age],(newValue,oldValue)=>{
				console.log('person的name或age变化了',newValue,oldValue)
			})  */

			//特殊情况
			/* watch(()=>person.job,(newValue,oldValue)=>{
				console.log('person的job变化了',newValue,oldValue)
			},{deep:true}) //此处由于监视的是reactive素定义的对象中的某个属性,所以deep配置有效 */


			//返回一个对象(常用)
			return {
				sum,
				msg,
				person
			}
		}
	}
</script>


3.watchEffect函数

  • watch的套路是:既要指明监视的属性,也要指明监视的回调。

  • watchEffect的套路是:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性。

  • watchEffect有点像computed:

    • 但computed注重的计算出来的值(回调函数的返回值),所以必须要写返回值。
    • 而watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值。
    //watchEffect所指定的回调中用到的数据只要发生变化,则直接重新执行回调。
    watchEffect(()=>{
        const x1 = sum.value
        const x2 = person.age
        console.log('watchEffect配置的回调执行了')
    })
    

如:

<template>
	<h2>当前求和为:{{sum}}</h2>
	<button @click="sum++">点我+1</button>
	<hr>
	<h2>当前的信息为:{{msg}}</h2>
	<button @click="msg+='!'">修改信息</button>
	<hr>
	<h2>姓名:{{person.name}}</h2>
	<h2>年龄:{{person.age}}</h2>
	<h2>薪资:{{person.job.j1.salary}}K</h2>
	<button @click="person.name+='~'">修改姓名</button>
	<button @click="person.age++">增长年龄</button>
	<button @click="person.job.j1.salary++">涨薪</button>
</template>

<script>
	import {ref,reactive,watch,watchEffect} from 'vue'
	export default {
		name: 'Demo',
		setup(){
			//数据
			let sum = ref(0)
			let msg = ref('你好啊')
			let person = reactive({
				name:'张三',
				age:18,
				job:{
					j1:{
						salary:20
					}
				}
			})

			//监视
			/* watch(sum,(newValue,oldValue)=>{
				console.log('sum的值变化了',newValue,oldValue)
			},{immediate:true}) */

			watchEffect(()=>{
				const x1 = sum.value
				const x2 = person.job.j1.salary
				console.log('watchEffect所指定的回调执行了')
			})

			//返回一个对象(常用)
			return {
				sum,
				msg,
				person
			}
		}
	}
</script>


8.生命周期

在这里插入图片描述
在这里插入图片描述

  • Vue3.0中可以继续使用Vue2.x中的生命周期钩子,但有有两个被更名:
    • beforeDestroy改名为 beforeUnmount
    • destroyed改名为 unmounted
  • Vue3.0也提供了 Composition API 形式的生命周期钩子,与Vue2.x中钩子对应关系如下:
    • beforeCreate===>setup()
    • created=======>setup()
    • beforeMount ===>onBeforeMount
    • mounted=======>onMounted
    • beforeUpdate===>onBeforeUpdate
    • updated =======>onUpdated
    • beforeUnmount ==>onBeforeUnmount
    • unmounted =====>onUnmounted

注意:组合式API比配置项优先级高
如:

<template>
	<h2>当前求和为:{{sum}}</h2>
	<button @click="sum++">点我+1</button>
</template>

<script>
	import {ref,onBeforeMount,onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted} from 'vue'
	export default {
		name: 'Demo',
		
		setup(){
			console.log('---setup---')
			//数据
			let sum = ref(0)

			//通过组合式API的形式去使用生命周期钩子
			onBeforeMount(()=>{
				console.log('---onBeforeMount---')
			})
			onMounted(()=>{
				console.log('---onMounted---')
			})
			onBeforeUpdate(()=>{
				console.log('---onBeforeUpdate---')
			})
			onUpdated(()=>{
				console.log('---onUpdated---')
			})
			onBeforeUnmount(()=>{
				console.log('---onBeforeUnmount---')
			})
			onUnmounted(()=>{
				console.log('---onUnmounted---')
			})

			//返回一个对象(常用)
			return {sum}
		},
		//通过配置项的形式使用生命周期钩子
		//#region 
		beforeCreate() {
			console.log('---beforeCreate---')
		},
		created() {
			console.log('---created---')
		},
		beforeMount() {
			console.log('---beforeMount---')
		},
		mounted() {
			console.log('---mounted---')
		},
		beforeUpdate(){
			console.log('---beforeUpdate---')
		},
		updated() {
			console.log('---updated---')
		},
		beforeUnmount() {
			console.log('---beforeUnmount---')
		},
		unmounted() {
			console.log('---unmounted---')
		},
		//#endregion
	}
</script>

9.自定义hook函数

  • 什么是hook?—— 本质是一个函数,把setup函数中使用的Composition API进行了封装。

  • 类似于vue2.x中的mixin。

  • 自定义hook的优势: 复用代码, 让setup中的逻辑更清楚易懂。

如:
1、创建usePoint.js

import {reactive,onMounted,onBeforeUnmount} from 'vue'
export default function (){
	//实现鼠标“打点”相关的数据
	let point = reactive({
		x:0,
		y:0
	})

	//实现鼠标“打点”相关的方法
	function savePoint(event){
		point.x = event.pageX
		point.y = event.pageY
		console.log(event.pageX,event.pageY)
	}

	//实现鼠标“打点”相关的生命周期钩子
	onMounted(()=>{
		window.addEventListener('click',savePoint)
	})

	onBeforeUnmount(()=>{
		window.removeEventListener('click',savePoint)
	})

	return point
}

2、在需要的地方引入调用即可

<template>
	<h2>我是Test组件</h2>
	<h2>当前点击时鼠标的坐标为:x:{{point.x}},y:{{point.y}}</h2>
</template>

<script>
	import usePoint from '../hooks/usePoint'
	export default {
		name:'Test',
		setup(){
			const point = usePoint()
			return {point}
		}
	}
</script>

10.toRef

  • 作用:创建一个 ref 对象,其value值指向另一个对象中的某个属性。

  • 语法:const name = toRef(person,'name')

  • 应用: 要将响应式对象中的某个属性单独提供给外部使用时。

  • 扩展:toRefstoRef功能一致,但可以批量创建多个 ref 对象,语法:toRefs(person)

如:

<template>
	<h4>{{person}}</h4>
	<h2>姓名:{{name}}</h2>
	<h2>年龄:{{age}}</h2>
	<h2>薪资:{{job.j1.salary}}K</h2>
	<button @click="name+='~'">修改姓名</button>
	<button @click="age++">增长年龄</button>
	<button @click="job.j1.salary++">涨薪</button>
</template>

<script>
	import {ref,reactive,toRef,toRefs} from 'vue'
	export default {
		name: 'Demo',
		setup(){
			//数据
			let person = reactive({
				name:'张三',
				age:18,
				job:{
					j1:{
						salary:20
					}
				}
			})

			// const name1 = person.name
			// console.log('%%%',name1)

			// const name2 = toRef(person,'name')
			// console.log('####',name2)

			const x = toRefs(person)
			console.log('******',x)

			//返回一个对象(常用)
			return {
				person,
				// name:toRef(person,'name'),
				// age:toRef(person,'age'),
				// salary:toRef(person.job.j1,'salary'),
				...toRefs(person)
			}
		}
	}
</script>


三、其它 Composition API

1.shallowReactive 与 shallowRef

  • shallowReactive:只处理对象最外层属性的响应式(浅响应式)。

  • shallowRef:只处理基本数据类型的响应式, 不进行对象的响应式处理。

  • 什么时候使用?

    • 如果有一个对象数据,结构比较深, 但变化时只是外层属性变化 ===> shallowReactive。
    • 如果有一个对象数据,后续功能不会修改该对象中的属性,而是生新的对象来替换 ===> shallowRef。

如:

<template>
	<h4>当前的x.y值是:{{x.y}}</h4>
	<button @click="x={y:888}">点我替换x</button>
	<button @click="x.y++">点我x.y++</button>
	<hr>
	<h4>{{person}}</h4>
	<h2>姓名:{{name}}</h2>
	<h2>年龄:{{age}}</h2>
	<h2>薪资:{{job.j1.salary}}K</h2>
	<button @click="name+='~'">修改姓名</button>
	<button @click="age++">增长年龄</button>
	<button @click="job.j1.salary++">涨薪</button>
</template>

<script>
	import {ref,reactive,toRef,toRefs,shallowReactive,shallowRef} from 'vue'
	export default {
		name: 'Demo',
		setup(){
			//数据
			// let person = shallowReactive({ //只考虑第一层数据的响应式
			let person = reactive({
				name:'张三',
				age:18,
				job:{
					j1:{
						salary:20
					}
				}
			})
			let x = shallowRef({
				y:0
			})
			console.log('******',x)

			//返回一个对象(常用)
			return {
				x,
				person,
				...toRefs(person)
			}
		}
	}
</script>


2.readonly 与 shallowReadonly

  • readonly: 让一个响应式数据变为只读的(深只读)。
  • shallowReadonly:让一个响应式数据变为只读的(浅只读)。
  • 应用场景: 不希望数据被修改时。

如:

<template>
	<h4>当前求和为:{{sum}}</h4>
	<button @click="sum++">点我++</button>
	<hr>
	<h2>姓名:{{name}}</h2>
	<h2>年龄:{{age}}</h2>
	<h2>薪资:{{job.j1.salary}}K</h2>
	<button @click="name+='~'">修改姓名</button>
	<button @click="age++">增长年龄</button>
	<button @click="job.j1.salary++">涨薪</button>
</template>

<script>
	import {ref,reactive,toRefs,readonly,shallowReadonly} from 'vue'
	export default {
		name: 'Demo',
		setup(){
			//数据
			let sum = ref(0)
			let person = reactive({
				name:'张三',
				age:18,
				job:{
					j1:{
						salary:20
					}
				}
			})

			person = readonly(person)
			// person = shallowReadonly(person)
			// sum = readonly(sum)
			// sum = shallowReadonly(sum)

			//返回一个对象(常用)
			return {
				sum,
				...toRefs(person)
			}
		}
	}
</script>

3.toRaw 与 markRaw

  • toRaw:
    • 作用:将一个由reactive生成的响应式对象转为普通对象
    • 使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新。
  • markRaw:
    • 作用:标记一个对象,使其永远不会再成为响应式对象。
    • 应用场景:
      1. 有些值不应被设置为响应式的,例如复杂的第三方类库等。
      2. 当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能。
<template>
	<h4>当前求和为:{{sum}}</h4>
	<button @click="sum++">点我++</button>
	<hr>
	<h2>姓名:{{name}}</h2>
	<h2>年龄:{{age}}</h2>
	<h2>薪资:{{job.j1.salary}}K</h2>
	<h3 v-show="person.car">座驾信息:{{person.car}}</h3>
	<button @click="name+='~'">修改姓名</button>
	<button @click="age++">增长年龄</button>
	<button @click="job.j1.salary++">涨薪</button>
	<button @click="showRawPerson">输出最原始的person</button>
	<button @click="addCar">给人添加一台车</button>
	<button @click="person.car.name+='!'">换车名</button>
	<button @click="changePrice">换价格</button>
</template>

<script>
	import {ref,reactive,toRefs,toRaw,markRaw} from 'vue'
	export default {
		name: 'Demo',
		setup(){
			//数据
			let sum = ref(0)
			let person = reactive({
				name:'张三',
				age:18,
				job:{
					j1:{
						salary:20
					}
				}
			})

			function showRawPerson(){
				const p = toRaw(person)
				p.age++
				console.log(p)
			}

			function addCar(){
				let car = {name:'奔驰',price:40}
				person.car = markRaw(car)
			}

			function changePrice(){
				person.car.price++
				console.log(person.car.price)
			}

			//返回一个对象(常用)
			return {
				sum,
				person,
				...toRefs(person),
				showRawPerson,
				addCar,
				changePrice
			}
		}
	}
</script>


4.customRef

  • 作用:创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制。

  • 实现防抖效果:

 <template>
  	<input type="text" v-model="keyword">
  	<h3>{{keyword}}</h3>
  </template>
  
  <script>
  	import {ref,customRef} from 'vue'
  	export default {
  		name:'Demo',
  		setup(){
  			// let keyword = ref('hello') //使用Vue准备好的内置ref
  			//自定义一个myRef
  			function myRef(value,delay){
  				let timer
  				//通过customRef去实现自定义
  				return customRef((track,trigger)=>{
  					return{
  						get(){
  							track() //告诉Vue这个value值是需要被“追踪”的
  							return value
  						},
  						set(newValue){
  							clearTimeout(timer)
  							timer = setTimeout(()=>{
  								value = newValue
  								trigger() //告诉Vue去更新界面
  							},delay)
  						}
  					}
  				})
  			}
  			let keyword = myRef('hello',500) //使用程序员自定义的ref
  			return {
  				keyword
  			}
  		}
  	}
  </script>

5.provide 与 inject

  • 作用:实现祖与后代组件间通信

  • 套路:父组件有一个 provide 选项来提供数据,后代组件有一个 inject 选项来开始使用这些数据

  • 具体写法:

    1. 祖组件中:

      setup(){
      	......
          let car = reactive({name:'奔驰',price:'40万'})
          provide('car',car)
          ......
      }
      
    2. 后代组件中:

      setup(props,context){
      	......
          const car = inject('car')
          return {car}
      	......
      }
      

如:
App.vue

<template>
	<div class="app">
		<h3>我是App组件(祖),{{name}}--{{price}}</h3>
		<Child/>
	</div>
</template>

<script>
	import { reactive,toRefs,provide } from 'vue'
	import Child from './components/Child.vue'
	export default {
		name:'App',
		components:{Child},
		setup(){
			let car = reactive({name:'奔驰',price:'40W'})
			provide('car',car) //给自己的后代组件传递数据
			return {...toRefs(car)}
		}
	}
</script>

<style>
	.app{
		background-color: gray;
		padding: 10px;
	}
</style>

Child.vue组件

<template>
	<div class="child">
		<h3>我是Child组件(子)</h3>
		<Son/>
	</div>
</template>

<script>
	import {inject} from 'vue'
	import Son from './Son.vue'
	export default {
		name:'Child',
		components:{Son},
		/* setup(){
			let x = inject('car')
			console.log(x,'Child-----')
		} */
	}
</script>

<style>
	.child{
		background-color: skyblue;
		padding: 10px;
	}
</style>

Son组件(孙)

<template>
	<div class="son">
		<h3>我是Son组件(孙),{{car.name}}--{{car.price}}</h3>
	</div>
</template>

<script>
	import {inject} from 'vue'
	export default {
		name:'Son',
		setup(){
			let car = inject('car')
			return {car}
		}
	}
</script>

<style>
	.son{
		background-color: orange;
		padding: 10px;
	}
</style>

6.响应式数据的判断

  • isRef: 检查一个值是否为一个 ref 对象
  • isReactive: 检查一个对象是否是由 reactive 创建的响应式代理
  • isReadonly: 检查一个对象是否是由 readonly 创建的只读代理
  • isProxy: 检查一个对象是否是由 reactive 或者 readonly 方法创建的代理
<template>
	<h3>我是App组件</h3>
</template>

<script>
	import {ref, reactive,toRefs,readonly,isRef,isReactive,isReadonly,isProxy } from 'vue'
	export default {
		name:'App',
		setup(){
			let car = reactive({name:'奔驰',price:'40W'})
			let sum = ref(0)
			let car2 = readonly(car)

			console.log(isRef(sum))
			console.log(isReactive(car))
			console.log(isReadonly(car2))
			console.log(isProxy(car))
			console.log(isProxy(sum))

			
			return {...toRefs(car)}
		}
	}
</script>

<style>
	.app{
		background-color: gray;
		padding: 10px;
	}
</style>

四、Composition API 的优势

1.Options API 存在的问题

使用传统OptionsAPI中,新增或者修改一个需求,就需要分别在data,methods,computed里修改 。

.Composition API 的优势

我们可以更加优雅的组织我们的代码,函数。让相关功能的代码更加有序的组织在一起。(但要利用hook函数)

五、新的组件

1.Fragment

  • 在Vue2中: 组件必须有一个根标签
  • 在Vue3中: 组件可以没有根标签, 内部会将多个标签包含在一个Fragment虚拟元素中
  • 好处: 减少标签层级, 减小内存占用

2.Teleport

  • 什么是Teleport?—— Teleport 是一种能够将我们的组件html结构移动到指定位置的技术。

    <teleport to="移动位置">
    	<div v-if="isShow" class="mask">
    		<div class="dialog">
    			<h3>我是一个弹窗</h3>
    			<button @click="isShow = false">关闭弹窗</button>
    		</div>
    	</div>
    </teleport>
    

如:

<template>
	<div>
		<button @click="isShow = true">点我弹个窗</button>
		<teleport to="body">
			<div v-if="isShow" class="mask">
				<div class="dialog">
					<h3>我是一个弹窗</h3>
					<h4>一些内容</h4>
					<h4>一些内容</h4>
					<h4>一些内容</h4>
					<button @click="isShow = false">关闭弹窗</button>
				</div>
			</div>
		</teleport>
	</div>
</template>

<script>
	import {ref} from 'vue'
	export default {
		name:'Dialog',
		setup(){
			let isShow = ref(false)
			return {isShow}
		}
	}
</script>

<style>
	.mask{
		position: absolute;
		top: 0;bottom: 0;left: 0;right: 0;
		background-color: rgba(0, 0, 0, 0.5);
	}
	.dialog{
		position: absolute;
		top: 50%;
		left: 50%;
		transform: translate(-50%,-50%);
		text-align: center;
		width: 300px;
		height: 300px;
		background-color: green;
	}
</style>

3.Suspense

  • 等待异步组件时渲染一些额外内容,让应用有更好的用户体验

  • 使用步骤:

    • 异步引入组件

      import {defineAsyncComponent} from 'vue'
      const Child = defineAsyncComponent(()=>import('./components/Child.vue'))
      
    • 使用Suspense包裹组件,并配置好defaultfallback

      <template>
      	<div class="app">
      		<h3>我是App组件</h3>
      		<Suspense>
      			<template v-slot:default>
      				<Child/>
      			</template>
      			<template v-slot:fallback>
      				<h3>加载中.....</h3>
      			</template>
      		</Suspense>
      	</div>
      </template>
      

如:
App.vue

<template>
	<div class="app">
		<h3>我是App组件</h3>
		<Suspense>
			<template v-slot:default>
				<Child/>
			</template>
			<template v-slot:fallback>
				<h3>稍等,加载中...</h3>
			</template>
		</Suspense>
	</div>
</template>

<script>
	// import Child from './components/Child'//静态引入
	import {defineAsyncComponent} from 'vue' //定义一个异步组件
	const Child = defineAsyncComponent(()=>import('./components/Child')) //异步引入
	export default {
		name:'App',
		components:{Child},
	}
</script>

<style>
	.app{
		background-color: gray;
		padding: 10px;
	}
</style>

clild.vue

<template>
	<div class="child">
		<h3>我是Child组件</h3>
		{{sum}}
	</div>
</template>

<script>
	import {ref} from 'vue'
	export default {
		name:'Child',
		async setup(){
			let sum = ref(0)
			let p = new Promise((resolve,reject)=>{
				setTimeout(()=>{
					resolve({sum})
				},3000)
			})
			return await p
		}
	}
</script>

<style>
	.child{
		background-color: skyblue;
		padding: 10px;
	}
</style>

六、其他

1.全局API的转移

  • Vue 2.x 有许多全局 API 和配置。

    • 例如:注册全局组件、注册全局指令等。

      //注册全局组件
      Vue.component('MyButton', {
        data: () => ({
          count: 0
        }),
        template: '<button @click="count++">Clicked {{ count }} times.</button>'
      })
      
      //注册全局指令
      Vue.directive('focus', {
        inserted: el => el.focus()
      }
      
  • Vue3.0中对这些API做出了调整:

    • 将全局的API,即:Vue.xxx调整到应用实例(app)上

      2.x 全局 API(Vue3.x 实例 API (app)
      Vue.config.xxxxapp.config.xxxx
      Vue.config.productionTip移除
      Vue.componentapp.component
      Vue.directiveapp.directive
      Vue.mixinapp.mixin
      Vue.useapp.use
      Vue.prototypeapp.config.globalProperties

2.其他改变

  • data选项应始终被声明为一个函数。

  • 过度类名的更改:

    • Vue2.x写法

      .v-enter,
      .v-leave-to {
        opacity: 0;
      }
      .v-leave,
      .v-enter-to {
        opacity: 1;
      }
      
    • Vue3.x写法

      .v-enter-from,
      .v-leave-to {
        opacity: 0;
      }
      
      .v-leave-from,
      .v-enter-to {
        opacity: 1;
      }
      
  • 移除keyCode作为 v-on 的修饰符,同时也不再支持config.keyCodes

  • 移除v-on.native修饰符

    • 父组件中绑定事件

      <my-component
        v-on:close="handleComponentEvent"
        v-on:click="handleNativeClickEvent"
      />
      
    • 子组件中声明自定义事件

      <script>
        export default {
          emits: ['close']
        }
      </script>
      
  • 移除过滤器(filter)

    过滤器虽然这看起来很方便,但它需要一个自定义语法,打破大括号内表达式是 “只是 JavaScript” 的假设,这不仅有学习成本,而且有实现成本!建议用方法调用或计算属性去替换过滤器。

第三方库

提供第三方库的网站:https://www.bootcdn.cn/
好用的第三方库:
dayjs是一个轻量的处理时间和日期的 JavaScript 库,和 Moment.js 的 API 设计保持完全一样

浏览器本地存储

webStorage: SessionStorage和LocalStorage都称为webStorage
1.存储内容大小一般支持5MB左右 (不同浏览器可能还不一样)
2.浏览器端通过 Window.sessionStorage 和 Window.localStorage 属性来实现本地存储机制。
3.相关API:

1. xxxxxStorage.setItem(' key", 'value');
该方法接受一个键和值作为参数,会把键值对添加到存储中,如果键名存在,则更新其对应的值。
2. xxxxxStorage.getItem('person');
该方法接受一个键名作为参数,返回键名对应的值。
3.xxxxxStorage.removeItem( " key");
该方法接受一个键名作为参数,并把该键名从存储中删除。
4.xxxxxStorage.clear()
该方法会清空存储中的所有数据

备注:
1.SessionStorage存储的内容会随着浏览器窗口关闭而消失
2.LocalStorage存储的内容,需要手动清除才会消失。
3.xxxxxStorage.getItem(xxx) 如果xxx对应的value获取不到,那么getltem的返回值是null。
4.JsoN.parse(nu11)的结果依然是null。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/174776.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

专访特斯拉工程师杨硕:跟着机器人上天入地、探索地外行星丨智源独家

导读 十几岁时&#xff0c;他痴迷《终结者》&#xff0c;曾在百科全书中窥见卡内基梅隆大学机械臂的介绍&#xff0c;从而得知了研究机器人「圣地」的存在。 在CMU&#xff0c;他深耕足式机器人感知定位算法&#xff0c;期待未来涉足太空&#xff0c;走上火星。 在大疆&#xf…

水果音乐制作软件FL Studio21.2中文版新功能介绍

FL Studio21.2中文版&#xff0c;一般又称水果音乐制作软件。 FL Studio 21.2简称FL&#xff0c;全称FruityLoopsStudio&#xff0c;因此国人习惯叫它"水果"。它让你的计算机就像是全功能的录音室&#xff0c;大混音盘&#xff0c;非常先进的制作工具&#xff0c;让…

【C语言】数据结构——栈和队列实例探究

&#x1f497;个人主页&#x1f497; ⭐个人专栏——数据结构学习⭐ &#x1f4ab;点击关注&#x1f929;一起学习C语言&#x1f4af;&#x1f4ab; 目录 导读&#xff1a;一、 栈1. 栈的概念及结构2. 栈的实现3. 实现代码3.1 定义结构体3.2 初始化栈3.3 销毁栈3.4 入栈3.5 出栈…

java io流中为什么使用缓冲流就能加快文件读写速度

FileInputStream的read方法底层确实是通过调用JDK层面的read方法&#xff0c;并且这个JDK层面的read方法底层是使用C语言编写的&#xff0c;以实现高效的文件读取功能。但是它会涉及多次内核态与操作系统交互。当我们使用FileInputStream的read方法读取文件时&#xff0c;首先会…

微服务 Spring Cloud 8,开源RPC框架如何选型?

目录 一、开源RPC框架有哪些&#xff1f;1、跟语言平台绑定的开源RPC框架2、跨语言平台的开源RPC框架 二、跟语言平台绑定的开源RPC框架 -- Dubbo1、Dubbo的架构主要包含四个角色2、Dubbo的调用框架是如何实现的&#xff1f; 三、如何选择&#xff1f;四、跨语言平台的开源RPC框…

继承【C++】

继承【C】 一.什么是继承&#xff1f;二. 继承的方式与权限三. 继承中的成员3.0 基类和派生类中的重名成员i. 限定符ii. 隐藏 3.1 继承与默认成员函数i. 默认构造ii. 析构函数 3.2 继承与友元函数3.3 继承与静态成员变量 四. 基类和派生类的赋值五. 多继承5.1 菱形继承5.2 菱形…

CFCA证书——基于SM2/3算法的安全信任

在中国金融认证中心&#xff08;CFCA&#xff09;发行的证书中&#xff0c;采用了最新的国密SM2/3算法来提供更高的安全保障。这一创新举措进一步增强了我国网络安全能力&#xff0c;并为用户提供了一种更可靠、更安全的选择。 SM2/3算法是中国自主研发的非对称加密算法&#…

瑞格心理咨询系统设置多个管理员的操作方法

使用瑞格心理咨询系统&#xff0c;需要设置多个admin权限的管理员账号来管理&#xff0c;咨询厂家答复只能有1个管理员&#xff0c;个人觉得不可能&#xff0c;于是开始折腾。 解决办法&#xff1a; 在没有数据字典的情况下&#xff0c; 通过遍历数据库&#xff0c;发现用户信…

python趣味编程-5分钟实现一个石头剪刀布游戏(含源码、步骤讲解)

Python 中的石头剪刀布代码是 使用Tkinter和图形用户界面(GUI)设计的。 Python 石头剪刀布游戏是使用Python 编程语言开发的简单桌面应用程序。 项目系统文件包含资源文件和Python脚本。游戏画面流畅,用户控制起来很容易。

大数据:SAS数据分析1,数据步,和过程步

大数据&#xff1a;SAS数据分析 2022找工作是学历、能力和运气的超强结合体&#xff0c;遇到寒冬&#xff0c;大厂不招人&#xff0c;可能很多算法学生都得去找开发&#xff0c;测开 测开的话&#xff0c;你就得学数据库&#xff0c;sql&#xff0c;oracle&#xff0c;尤其sql…

2023年DevOps国际峰会暨BizDevOps企业峰会(DOIS北京站)-核心PPT资料下载

一、峰会简介 在数字化转型的大背景下&#xff0c;企业选择实践 DevOps 来提升 IT 效能成为常态&#xff0c;BizDevOps 作为企业自身数字化变革的重要主题之一&#xff0c;需要全行业共同努力促进繁荣和发展。从 DevOps 到 BizDevOps&#xff0c;业务与技术如何融合&#xff1…

JAVAEE---多线程

内核 内核时操作系统的核心 操作系统有内核态和用户态&#xff0c;像我们平时所用到的qq音乐&#xff0c;微信等都属于用户态执行的程序。那么qq音乐播放音乐需要用到扬声器&#xff0c;扬声器的操作就是在内核空间进行操作&#xff0c;用户态不能对其进行操作。 操作系统 …

移远通信推出六款新型天线,为物联网客户带来更丰富的产品选择

近日&#xff0c;移远通信重磅推出六款新型天线&#xff0c;覆盖5G、非地面网络&#xff08;NTN&#xff09;等多种新技术&#xff0c;将为物联网终端等产品带来全新功能和更强大的连接性能。 移远通信COO张栋表示&#xff1a;“当前&#xff0c;物联网应用除了需要高性能的天线…

基于卷尾猴算法优化概率神经网络PNN的分类预测 - 附代码

基于卷尾猴算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于卷尾猴算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于卷尾猴优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要&#xff1a;针对PNN神经网络…

雷电模拟器报错:g_bGuestPoweroff.fastpipeapi. cpp_1153_1161

文章目录 一、报错详情&#xff1a;二、解决&#xff1a;【1】设置Windows功能【2】设置cmd&#xff08;管理员身份&#xff09;【3】重启电脑 三、windows10其中1809版本出现1153、1161&#xff0c;需要关闭内核隔离 一、报错详情&#xff1a; 二、解决&#xff1a; 【1】设置…

909-2014-T2

文章目录 1.原题2.算法思想3.关键代码4.完整代码5.运行结果 1.原题 二叉树采用二叉链表存储结构&#xff0c;设计算法&#xff0c;判断二叉树是否为满二叉树。叙述算法思想并给出算法实现。 2.算法思想 通过一次遍历&#xff0c;得到结点个数和树的高度。用结点个数和树的高…

什么是高防CDN?有什么优势?

德迅高防CDN技术概述 随着互联网的快速发展&#xff0c;网络安全问题越来越受到人们的关注。高防CDN(Content Delivery Network)作为一种有效的网络安全防御手段&#xff0c;在企业和个人网站中得到了广泛应用。本文将详细介绍高防CDN的技术原理、防御原理、优点和应用场景&am…

TransmittableThreadLocal - 线程池中也可以传递参数了

一、InheritableThreadLocal的不足 InheritableThreadLocal可以用于主子线程之间传递参数&#xff0c;但是它必须要求在主线程中手动创建的子线程才可以获取到主线程设置的参数&#xff0c;不能够通过线程池的方式调用。 但是现在我们实际的项目开发中&#xff0c;一般都是采…

用 HLS 实现 UART

用 HLS 实现 UART 介绍 UART 是一种旧的串行通信机制&#xff0c;但仍在很多平台中使用。它在 HDL 语言中的实现并不棘手&#xff0c;可以被视为本科生的作业。在这里&#xff0c;我将通过这个例子来展示在 HLS 中实现它是多么容易和有趣。 因此&#xff0c;从概念上讲&#xf…

秋招JAVA面经总结

面试的范围是Java基础+Java并发+Java框架+mysql+网络。 Java基础 重载与重写有什么区别? 重载(Overloading)指的是在同一个类中,可以有多个同名方法,它们具有不同的参数列表(参数类型、参数个数或参数顺序不同),编译器根据调用时的参数类型来决定调用哪个方法。 重写…