学习Vue这一个就够

news2024/11/26 23:21:11

1、淘宝镜像

 1: 解释一下 npm 为什么要配置淘宝镜像
      原因:因为node.js 默认使用的是国外的网站 。国内访问有一个跨国内局域网的操作。所以就会有时候很慢。这就跟为什么网站的静态资源有些会使用CDN 加速一样的
     淘宝镜像是什么?就是npm 很多的插件淘宝已经下载好了放在公共的网站上 我们需要的时候去淘宝网上下载 和 国外的是一样  这样使用是提升了我们的下载速度。所以淘宝镜像其实是一个国外插件的 国内版本
 2:安装命令
     npm config set registry https://registry.npm.taobao.org
 3:查看是否安装成功 
    npm info express
 4:使用命令 
    npm install -g cnpm --registry=https://registry.npm.taobao.org
     cnpm install <插件名>

3、安装vue的调试工具

1、插件已经下载好,直接拖着往谷歌浏览器的扩展程序里面,记得是开发者模式(教学案例)

1、理解什么是Vue

0:官网https://cn.vuejs.org/guide/introduction.html
  https://cdn.jsdelivr.net/npm/vue@2  压缩版
  https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js  生产版
1、Vue.js是目前最火的框架,是前端的主流框架之一,和Angular。js和React.js
并称为三大主流框架
2、Vue.js是一套构建用户界面的框架,只关注图层,不仅易于上手,HIA方便与第三方库或既有项目整合(有配套的第三方类库,可以整合起来做大型项目的开发
3、使用vue往html页面中填充数据,非常的方便。框架都是现成的解决方案,按照框架的规范,去便携自己的业务功能
4、主要就是学习vue的指令、组件、路由、vuex
5、vue的特性:
   1、数据驱动视图
   2、双向数据绑定

2、如何学习Vue

1、能够使用Vue的指令完成页面结构的渲染
2、能够使用Vue的调试工具辅助Vue的开发
3、了解什么是过滤器和侦听器,能够在实际开发中解决问题
4、什么是计算属性以及计算属性的用法
5、axios的基本用法,使用axios发起ajax请求
6、vue-cli,脚手架的安装和使用,生成工程化的Vue项目
7、组件的注册与使用,掌握vue单文件组件的基本用法
8、组件的props自定义属性
9、解决组件样式冲突
10、学习组件的生命周期,掌握组件声明周期的执行顺序和应用场景
11、组件之间的通讯(数据共享),组件之间通讯的三种方式
12、学习ref应用DOM元素和组件实例,能够使用ref获取页面上的DOM、或组件的引用
13、$nxtTick的基本使用,能够知道$nxtTick的应用场景并合理的使用
14、学习动态组件的使用,能够使用keep-alive实现组件的缓存
15、自定义指令
16、学习ESLint的使用,能够了解ESLint的语法规则
17、学习使用(默认插槽、具名插槽、作用域插槽),能够使用插槽提高组件的复用性
18、学习路由的基本配置与使用,能够在项目中安装和配置路由
19、路由重定向
20、嵌套路由、动态路由
21、编程式导航、路由导航守卫,能够使用路由实现单页面程序的开发。使用导航收尾控制路由的访问权限
22、学习Vant,掌握Vant组件库的基本使用,知道如何封装axios请求模块

3、数据驱动视图

1、在vue的页面中,vue会监听数据的变化,从而自动重新渲染页面的结构
  页面结构<-vue(监听数据的变化,数据发生改变)<-页面发生变化
  好处:当页面数据发生变化时,页面会自动重新渲染,只需要管理好数据
  注意:数据驱动视图是单向的数据绑定
  总结:数据的变化会驱动视图自动更新,

3-1、数据代理

1、数据代理:通过一个对象代理另一个对象中属性的操作

2、vue中采用数据代理Object.defineProperty中的setter和getter进行数据代理和数据的响应

3-2、VueComponent

1、整个项目只会有一个Vue实例对象vm,其他的都是VueComponent实例对象,

2、VueComponent的实例对象,简称为vc,也称为组件实例对象,Vue的实例对象只有一个,称为vm

3、VueComponent可以通过Vue.extend()方法创建个很多个vc。在项目里面的组件实例对象就是通过这种方式创建的。就是vc

4、vm和vc很多东西都一样,但是vc可以有很多个,vm只能有一个,而且vm可以设置el指定渲染的容器,vc不行。

4、双向数据绑定

1、填写表单的时候,双向绑定可以在不操作DOM的前提下,自动把用户填写的内容同步到数据中。
    页面结构<->vue(监听数据的变化,数据发生改变)<->页面发生变化
    form表单负责采集数据,ajax负责提交数据
    用户不需要手动操作dom元素,来获取表单数据
    页面上表单采集的数据发生变化的时候,会被vue自动获取到,并更新到js数据中。

4-MVVM核心原理(数据驱动和双向绑定的原理)

1、MVVM是vue实现数据驱动视图和双向数据绑定的核心原理。MVVM指的   是Model、View、ViewModel,把每个页面都拆分成了这三个部分。
   Model表示当前页面渲染时所以来的数据源
   View表示当前页面所渲染的DOM解构
   ViewModel表示Vue的实例,是MVVM的核心。就是vue
2、ViewModel作为MVVM的核心,是把当前页面的数据源和页面的结构连接在了一起。

view 《--(自动更新,监听DOM变化)--》ViewMOdel《---(监听数据变化,自动同步)--》Model
  
  当数据源发生变化是,会被ViewModel监听到,VM会根据最新的数据源自动更新页面的结构。
  当表单的值发生变化时,也会被VM监听到,VM会把变化过的最新的值自动同del数据源中

在这里插入图片描述

5、Vue的版本

1.0版本基本被淘汰,
2.x版本是目前企业级项目开发中的主流版本
3.x的与2020年-09发布,现在已经是默认版本了,但是还没有普遍使用
vue的作者:尤雨溪,是中国人开发的前端框架
react和angular都是外国人开发
比较简单

6、Vue的基本使用

1、导入vue.js的script脚本文件
2、在页面中声明一个将要被vue所控制的DOM区域
3、创建一个vue实例
4、实例里面选择渲染的区域,定义要渲染的数据变量,
   <!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>
    <!-- 1、引入vue的js文件,有vue这个构造函数 -->
    <script src="../lib/vue.js"></script>
</head>

<body>
    <!-- div控制页面,需要被vue控制 -->
    <!-- 数据渲染:插值语法,可以把数据渲染到页面 -->
    <div id="app">{{username}}</div>
    <script>

        // 2、创建一个vue实例对象
        const vm = new Vue({
            // 3、el属性是固定的写法,表示vm实例要控制页面上的
            // 哪一个区域,接收的值是一个选择器
            el:'#app',
            // data是数据,所有用到的边浪在data里面定义
            // 然后渲染到页面上去
            // 这里是对象形式
            data:{
                username:'xiaohong'
            }

        })
    </script>


</body>

</html>
el指向的选择器选择的div就是view视图区域,data指向的对象就是MOdel数据源,new Vue是构造函数,创造了一个vm实例对象,就是viewModel。

7、配置Chrome浏览器的vue-devtools

1、插件已经下载好。可以直接使用

8、Vue的指令和过滤器

1、指令是VUe为开发者提供的模板语法,辅助开发者渲染页面的基本结构
2、指令按照不同的用途,分为六大类
   1、内容渲染指令
   2、属性绑定指令
   3、事件绑定指令
   4、双向绑定指令
   5、条件渲染指令
   6、列表渲染指令

8-1、内容渲染指令

1、内容渲染指令用来辅助开发者渲染DOM元素的文本内容,常用的内容渲染指令有3个
   1、v-text:会覆盖元素内部原有的内容,实际开发用的不多
   2、{{}}:插值表达式,不会覆盖默认文本,解决v-text的问题,可以在括号里面加点空格,内容占位符,只能用在内容节点,不能用在元素的属性节点。还
   3、v-html:其他两种只能渲染纯文本内容,该指令能把包含html标签的字符串渲染为页面的html元素,
2、总结:v-html渲染html标签,v-text和{{}}渲染纯文本,但是v-text会覆盖原本的内容,用的最多的是{{}}
      <!-- 1、插值语法 -->
        <!-- 纯文本 -->
        <div>{{username}}你好</div>



        <!-- 2、v-text -->
        <div v-text="username"> 你好</div>


        <!-- 3、v-html -->
        <div v-html="username">你好</div>
        <!-- 渲染html标签 -->
        <div v-html="ht"></div>

8-2、属性绑定指令(v-bind)

1、插值表达式只能用在内容节点中,不能用在元素节点中
2、v-bind:  给元素的属性动态绑定属性值,可以省略v-bind,保留 :
    <input type="text" placeholder="{{info}}">
      <!-- 在这里就表示给默认值绑定info -->
      <input type="text" v-bind:placeholder="info" >

      <img v-bind:src="photo" alt="">

      <!-- 简写方式:省略v-bind -->
      <img :src="photo" alt="">
3、注意:如果要用v-bind进行字符拼接,记得给字符串加上''
4、v-bind里面是js语句,

使用javascript表达式

1、在vue提供的模板渲染语法中,除了支持简单的数据值之外,还可以可以进行javascript表达式的运算
    <!-- 内容绑定指令 -->
        <!-- 1、加法 -->
        <div>{{ num +6 }}</div>

        <!-- 2、三元表达式 -->
        <div>{{ 5 > 4 ? 6 : 10}}</div>

        <!-- 3、字符串运算 -->
        <div>{{str.split("").reverse().join("")}}</div>


        <!-- 4、属性绑定指令 -->
        <!-- 字符串拼接 -->
        <div :id="'list-'+'h'"></div>

8-3、事件绑定指令

1、v-on事件绑定:为DOM元素绑定事件监听,
   语法格式 v-on:事件名称="事件函数的名字"
    <button v-on:click="add">按钮</button>
2、绑定事件并传参:绑定方法的时候可以加小括号,可以不加,如果要传递参数,就加小括号
    <div id="app">

        <div>{{ num }}</div>

        <button v-on:click="add(5)">按我+5</button>
    </div>
    <script>
        const vm = new Vue({
            el: "#app",
            data:function(){
                return {
                    num: 5,

                }
            },
            methods: {
                add(n){
                   this.num+=n
                }
                
            },
        })

    </script>
3、v-on的简写形式:@
   语法格式  @事件名称="事件函数的名字"
4、其他事件把原生事件的on去掉
  kyeup click
5、如果没有传递参数,那么我们默认可以接收到一个事件对象:e
<!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 src="../lib/vue.js"></script>
</head>

<body>
    <div id="app">

        <button @click="add">按我输出事件对象</button>
    </div>
    <script>
        const vm = new Vue({
            el: "#app",
            data:function(){
                return {
                    num: 5,

                }
            },
            methods: {
                add(e){
                   console.log(e);
                }
                
            },
        })
    </script>
</body>

</html>
6、可以通过这个事件对象获取到绑定事件的元素,然后对该元素进行修改
   <div id="app">      
        <button @click="add">按我输出事件对象</button>
    </div>

   <script>
        const vm = new Vue({
            el: "#app",
            data:function(){
                return {
                    num: 5,
                }
            },
            methods: {
                // 如果没有传递参数,那么我们默认可以接收到一个事件对象:e
                // 可以通过这个事件对象获取到绑定事件的元素,然后对该元素进行修改
                add(e){
                   e.target.style.backgroundColor="red"
                   e.target.style.fontSize="50px"
                }
                
            },
        })
    </script>
7、但是这样写太复杂了,所以vue给我们提供了一个内置变量:$event,就是原生的事件对象
   通过绑定函数的时候传递进去,函数就会接收到这个事件对象,固定写法
      <button @click="add(1,$event)">按我输出事件对象</button>
8、事件修饰符:阻止默认事件的发生
   以前:在函数里面写
   e.preventDefault()
   现在:在绑定事件的后面写 .prevent
      1、
       <a href="https://www.baidu.com" @click="run">阻止跳转</a><br>
         run(e){
                    e.preventDefault();
                    console.log("阻止成功");
                    

                },

     2、
       <a href="https://www.baidu.com" @click.prevent="run">事件修饰符阻止跳转</a>
        jump(){
                    console.log("阻止成功");
                    
            }
9、因为调用e.preventDefault和e.stopPropagation是常见需求,所以vue提供了事件修饰符的概念,来帮助程序员更方便的对事件的触发进行控制。常见的5个事件修饰符如下:
    .prevent:阻止默认行为
    .stop:阻止事件冒泡
    .once:只触发一次
    .self:只有在触发对象是当前元素自身触发时触发事件处理函数
    .caputre:以捕获模式触发当前的事件处理函数
10、按键修饰符:在监听键盘事件的时候,我们经常需要判断详细的按键,可以为键盘相关的时间添加按键修饰符,例如:
       <!-- 在key表示是”enter的时候,调用 .-->
       <input  type="text" @keyup.enter="ll($event)" > 


         <!-- 在key表示是”esc"的时候,调用 .-->
         <input  type="text" @keyup.esc="gg($event)" > 


8-4、双向绑定指令

1、vue提供了v-model双向数据绑定指令,快速获取表单的数据
2、双向绑定之后,vue的数据会随着表单的改变发生改变
       <input type="text" v-model="username">
        <div>{{username}}</div>
3、v-model指令只能和表单元素进行使用
   1、input输入框
   2、select
   3、textrea
4、为了方便对用户输入的内容进行处理,vue为v-model提供了3个修饰符
   .number:将用户的输入值转为数值类型
 <input type="text" v-model.number="n">+  <input type="text" v-model="s">={{n+s}}
   
  .trim:自动过滤用户的收尾空白字符
   <input type="text" v-model="search" @keyup.enter="ff">
   <input type="text" v-model.trim="search" @keyup.enter="ff">
    
  .lazy:在change时而非input时更新,在中间更新的时候不会改变,用的很少
   

8-5、条件渲染指令

1、条件渲染指令用来控制DOM的显示与隐藏。有两个条件渲染指令:
   v-if
   v-show
   <!-- v-if控制是否显示:满足条件 -->
    <span v-if="s==6">6</span>
    <span v-if="s==5">5</span>

     <br><br>

     <!-- v-show控制是否显示 -->
     <span v-show="s==6">6</span>
     <span v-show="s==5">5</span>
2、v-if 是通过让元素下树,隐藏该元素。动态的添加或移除元素,而v-show是通过style样式的display来隐藏或显示元素,如果要频繁的切换元素的显示形态,用v-show更好,如果有些元素不需要被展示,那么就用v-if。
3、  在实际开发中,不用考虑性能问题,直接使用v-if
4、还有一个v-else-if、v-else指令,v-else-if必须个v-if搭配使用,不然不会被识别

插件

1、vue 3 sinppets:
2、vetur:

8-6、列表渲染指令

1、v-for:列表渲染指令,基于一个数组来循环渲染一个列表结构,v-for需要用到iteminitems形式的特殊语法
  items是循环的数组
  item是循环数组的每一项元素
   <li v-for="item in arr">{{item}}</li>
    arr:["xiao","hong","lan","ming"]
2、谁在循环就给谁加v-for。
3、v-for还支持一个可选的第二参数,即当前项的索引,语法格式为(item,index) in items.  
        <!-- 3、索引。(item,index) in items. -->
        <ul>
            <li v-for="(item,index) in arr2">
                索引{{index}}
                {{item.name}}
            </li>
        </ul>

4、官方建议,如果用到了v-for指令,那么一定要绑定一个 :key 属性,可以把index作为key值。必须是字符串或者数字类型,而且key值不允许重复,不然会报错,最好是把id值作为key值,因为key值要求具有唯一性
        <!-- 4、 :key属性 -->
        <ul>
            <li v-for="(item,index) in arr2" :key="index">
                索引{{index}}
                {{item.name}}
            </li>
        </ul>
5、56-62跳过

9、过滤器

9-1、了解过滤器

1、过滤器(Filters):用于文本的格式化,可以用在两个地方,插值表达式和v-bind属性绑定,只能在vue3里面使用
2、过滤器应该被添加在javascript表达式的尾部,由管道符进行调用,管道符就是竖线 |
3、过滤器函数必须被定义在filters这个节点下面,这个节点对象和data平级,过滤器本质是一个函数,自己定义。过滤器中一定要有一个返回值。主要是为了进行文本的格式化,所以过滤器函数的传递的参数都是管道符前面那个值
     <span>
        {{ msg | capi }}
       </span>
<script>
 // 过滤器函数必须被定义在filters这个节点下面,
             // 这个节点对象和data平级,
            filters:{
            // 过滤器本质是一个函数,自己定义。过滤器中一定要有一个返回值

              capi(val){
                // 主要是为了进行文本的格式化
                  //在进行文本的首字母大写
       return   val.charAt(0).toUpperCase()+val.slice(1);
              }
            },
</script>
4、注意点:
    1、要定义到filters节点下,本质是一个函数
    2、在过滤器函数中,一定要有return,也就是返回值

9-2、全局过滤器和私有过滤器

1、定义在vue中的filters是私有过滤器,只能在当前的vue实例中使用
   
<body>
    <div id="app">
        <span>
            {{ msg | capi }}
        </span>

    </div>

    <div id="app2">
        <span>
            //使用不了这个capi过滤器,因为是定义在vm里面的,属于私有过滤器
            {{ msg | capi }}
        </span>

    </div>
    <script>

        const vm = new Vue({
            el: '#app',
            data: {
                msg: "hello world",
                arr: []

            },
            // 过滤器函数必须被定义在filters这个节点下面,
            // 这个节点对象和data平级,
            filters: {
                // 过滤器本质是一个函数,自己定义。过滤器中一定要有一个返回值

                capi(val) {
                    // 主要是为了进行文本的格式化
                    return val.charAt(0).toUpperCase() + val.slice(1);
                }
            },
            methods: {
                add() {
                    this
                }
            },
        })

        const vm2 = new Vue({
            el: "#app2",
            data: {
                msg: "hello world",
                arr: []

            },
        })
    </script>

</body>
2、被定义在vm实例里面的过滤器只能在当前所控制的el区域使用,如果希望在多个vue实例之间共享过滤器,则
  可以按照下面面的格式定义全局过滤器
   Vue.filter()方法接收两个参数,
    1):是全局过滤器的名字
    2):全局过滤器的处理函数
  
    <body>
        <div id="app">
          <span>
            {{ msg | capi }}
          </span>
         </div>

    <div id="app2">
        <span>
            {{ msg | capi }}
        </span>
    </div>

    <script>

        // 首先,这个全局过滤器应该定义在vm实例前面,
        // 然后使用Vue。filter定义全局过滤器
        Vue.filter('capi', (val) => {
            return val.charAt(0).toUpperCase() + val.slice(1)
        })

        const vm = new Vue({
            el: "#app",
            data: {
                msg: "hello"
            }
        })

        
        const vm2 = new Vue({
            el: "#app2",
            data: {
                msg: "hello world",
                arr: []

            },
        })

    </script>
</body>
3、如果全局过滤器和私有过滤器名字冲突,按照就近原则,优先使用私有过滤器
4、可以连续的使用多个过滤器
  {{  msg | capi | capi2  }}
  表示递增使用,后面一个过滤器使用前面一个过滤器使用后的结果
5、因为过滤器是函数,可以接收参数,但要注意,接收参数的时候要从第二个位置开始,因为第一个是接收的管道符的文本
6、vue3是没有过滤器的

10、侦听器

10-1、侦听器了解

1、侦听器:watch,监听数据的变化,针对数据的变化做特定的操作,侦听器本质上也是一个函数,监听谁就去watch里面定义一个和变量名字一样的函数,并做出响应。watch和data、methods是平级关系,表示
   监听数据的变化
   
<body>
    <div id="app">

        <div>
            {{num}}
        </div>
       <button @click="num++">num++</button>

    </div>
    <script>
        const vm = new Vue({

            el: "#app",
            data: {
                num:5

            },
            // 监听器,监视数值的变化
            watch:{
                num(){
                    console.log("num发生变化");
                }

            }
        })



    </script>
</body>
2、侦听器的函数里面可以有两个参数,分别是变量的新值和旧值
    watch:{
                num(newval,oldval){
                    console.log("num发生变化");
                    console.log(oldval);
                    console.log(newval);
                }

            }
3、侦听器的格式分为:
    1)、方法格式的侦听器
         缺点:无法在刚进入页面的时候,自动触发
              如果侦听的是一个对象,对象的属性发生变化不会触发监听
    2)、对象格式的侦听器
         优点:可以通过immediate选项让侦听器自动触发一次
              可以通过deep选项深度监听
   <script>
        watch:{
                // 对象格式的侦听器
                // 有一个handler的处理函数
                num:{
                    handler(){
                        console.log("num发生变化");

                    },
                    // 默认值是false
                    // ,控制监听器是否触动触发一次
                    immediate:true

                },
               
            }
       </script>
4、最好使用方法格式的,简单一点

10-2、深度监听

1、 对象格式的监听器可以通过deep:true这个属性监听对象的属性的改变,任何一个对象的属性发生改变都会触发这个监听器。而这是方法格式的监听器是做不到的

<body>
    <div id="app">


       <input type="text" v-model="student.name">

    </div>
    <script>
        const vm = new Vue({

            el: "#app",
            data: {
                student:{
                    name:"小红",
                    age:18,
                    score:99
                }

            },
           
            watch:{

            //   对象格式的监听器可以通过deep:true这个属性
            // 监听对象的属性的改变
                student:{
                    handler(){
                        console.log("student发生变化");

                    },
                    deep:true
                    

                },
               
            }
        })



    </script>
</body>
2、方法格式监听对象的子属性的变化,必须包裹一层单引号。
   'student.name'(){
                    console.log("'student.name'发生变化");
                }

11、计算属性

11-1、了解计算属性

1、计算属性是指通过一系列的运算后,最终得到一个属性值。这个动态计算出来的属性值可以被模板结构或methods方法使用
2、computed和el,data都是平级,都是属于vm这个实例对象的。是以对象的形式书写的。所有的计算属性都要定义到computed节点下面,计算属性在定义的时候,要定义成”方法格式”
3、使用说明:
  1、这个计算属性是计算某个具体的变量,是一个函数方法,我们要把它定义在computed里面。
  2、这个函数方法有一个返回值,
  3、直接使用这个函数,把他当做一个变量来使用,它的返回值就是计算的那个变量的值
   
<body>
    <div id="app">

        {{gaibian }}
    </div>

    <script>
        const vm = new Vue({

            el: "#app",
            data: {
                num: 5


            },
            // 计算属性
            computed: {
                gaibian() {
                    return this.num * 5

                }

            }
        })

    </script>
</body>
4、声明的时候是方法格式,使用的时候是变量格式
   好处:1)、实现代码的复用,
        2)、只要计算属性中的数据源发改变,使用到的计算属性也会改变

模板字符串

模板字符串语法:
	es5写法:
		需要使用大量的“”(双引号)和 + 来拼接才能得到我们需要的模版
		实例:
			"He is <b>" + person.name + "</b> and we wish to know his" + person.age + ".That is all" 
    
	es6写法:
		用`(反引号)标识,用${}将变量括起来
		实例:
			`He is <b> ${person.name} </b> and we wish to know his ${person.age} .that is all`
   就是说,用``反引号将整句话包裹进去,然后把变量用${}包裹
      {{`He is ${age}`}}

        <br>
        <br>

        {{ `${name} 已经 ${age} ,她的身高 ${h}`  }}
 

<!-- 属性渲染 -->

        <div :style="`background:rgb(${r},${g},${b})`"></div>

   

12、axios

1、axios是一个专注于数据请求的库,就是简化封装了ajax和promise的一个库,vue和react都会用这个
<body>
    <div id="app">
        <button @click="fn">按我获取数据</button>
        {{res}}

    </div>
    <script>
        const vm = new Vue({

            el: "#app",
            data: {
              res:{}

            },
            methods: {
                async fn() {
                    const p = await axios.get('http://182.92.193.159:5050/allcmc')
                    this.res=p.data                   
                }
            },

        })

    </script>
</body>
2、axios请求数据时,可以通过传递的参数获取自己想要的数据,传递参数使用data:{}
   const res = await axios.get("http://www.zt-it.com:5000/student", {
          params:{}
        })
3、axios的方法:axios.get() ,axios.post(),axios.delete(),axios.put()

13、Vue-cli

13-1、单页面应用程序

1、单页面就是一个Web网站里面只有一个唯一的一个html页面,素有的功能与交互都在这个唯一的一个页面内完成。
2、vue-cli是vue.js开发的标准工具,简化了程序员基于webpack创建工程化的vue项目的过程
3、中文官网地址:https://cli.vuejs.org/zh/
4、vue-cli是npm上的一个全局包,可以使用npm install命令安装
5、通过 vue create  项目的名称 就可以创建项目,最后一个表示让你自己手动选择需要的环境,然后进入下一步,*表示已经选择,第一项必须选择,表示vue的版本。按下空格键选择。eslint规定格式很严格,最好别选,bavel选择在独立的配置文件中

13-2、项目文件结构

 git:    是一个为git客户端增加git工具,用于存储自己的版本库(或者叫.svn根据自己的配置会有不同名字的版本库)
|- biuld:  vue2.0的配置,项目打包时候的配置文件(现如今的vue项目,可使用vue.config.js进行打包配置)
|- node_modules: node的一些基础依赖包,可能还有拓展的安装的别的插件(npm install下载的依赖包,主要是根据package.json获取对应的依赖包)
|- public: 存放一些主要的打包文件,如index.html等等,可以放置一些需要在index.html配置的文件

|- src:项目的主文件夹(vue是SPA单页面应用,可以看做是一个主页面的应用里面的内容组件详情可看vue 代码格式解析)
       |- assets:          资源文件,用于存放一些静态文件,如字体、图片、css样式之类的
       |- components: vue主要内容的公共组件,可以进行复用
       |- router:           设置路由,指定路由对应的组件
       |- route:            main.js中的router是通过router文件映射,而router下的index.js是项目链接的关键,通过设置路径将views中的vue文件关联起来
       |- main.js:项目的主js,全局使用的各种变量、js、插件都在此定义和引入;整个项目先加载src里的main.js,通过里面的app元素生成vue对象,再将router里面的路由加载进去,结果在app的vue中呈现
       |- app.vue:  项目的入口文件
       |- store:放置vuex需要的状态关联文件,设置公共的state等,是公共数据管理器,关联页面都可随时访问数据,是一个专为vue.js应用开发的状态管理模式,集中式存储管理应用的所有组件的状态
       |- test:              测试文件目录

|- .editorconfig:  是用来帮助开发者定义和维护代码风格(行尾结束符、缩进风格等)editorconfig配置文件网
|- .env: 全局默认配置文件,无论什么环境都会加载合并

.env.development:开发环境下的配置文件
.env.development: 开发环境下的配置文件
npm run serve(或者npm run dev 主要看 package.json) 会合并 .env 文件
.env.production: 生产环境下的配置文件
npm run build 会合并 .env 文件
|- .eslintignore:        指定忽略的文件,不需要eslint校验文件; eslint校验对不符合规范代码报错
|- .eslintrc.js:           eslintrc的配置文件,vue项目自带的文件,各个eslint配置项的作用;ESlint是一个检查代码的工具
|- .gitignore: 可以避免在提交代码时把我们不想上传的文件提交到git中; LICENSE:开源协议的说明
|- package.json:     记录作者、项目入口、项目描述、项目依赖等相关信息
|- pnpm-lock.yaml: 记录详细的依赖版本
|- postcss.config.js:插件,利用js插件对CSS进行转换
|- prettier.config.js: 配置文件,保持代码风格统一
|- README.md:     项目用的到的所有插件的json格式
|- stylelint.config.js:让CSS样式代码在编辑、保存、提交git的时候按规范进行检查及美化
|- tsconfig.json:      配置文件

13-2-1、项目外部文件
1、.gitignore:不接受git管理的文件或文件夹
2、babel.config.js:babel的控制文件。
3、package.json:配置的各种信息,依赖、各种信息。
   build:构建,最后一次编译,编译成html、js
   lint:进行语法检查
4、package-lock.json:包版本控制文件。
13-2-2、src
1、assets:存放项目的静态资源文件夹,比如:css样式表、图片资源
2、components:程序员封装的、可复用的组件。
3、main.js是项目的入口文件,整个项目的运行,要先执行main.js
4、app.vue 项目的主页,被main.js渲染到public的index.html文件里面
13-2-3、main.js构成:项目最先运行,渲染App组件
1、导入vue,得到vue构造函数
  import Vue from 'vue'

2、导入App.vue这个根组件,要把模板解构渲染到html页面中
  这个组件是所有组件的父组件。受vm管理
  import App from './App.vue'
  
3、关闭vue的生产提示
 Vue.config.productionTip = false

4、创建Vue的实例对象
 new Vue({
   //把render函数指定的组件渲染到html文件中
   //mounet是挂载,相当于:el:“#app”
   render: h => h(App),
 }).$mount('#app')

// 引入残缺版的vue,残缺版的vue不能自己渲染页面
// 只有完整版的vue能够渲染,
// vue由两部分组成:核心(生命周期、路由)+模板解析器
// 节省空间,残缺版去掉了模板解析器,通过render函数
// 去渲染页面
import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

new Vue({

  //render:渲染
  
//  h是一个函数,
// h去渲染元素

  render: h => h(App),


}).$mount('#app')

13-2-4、App.vue
1、所有组件的根组件
2、整个项目只有一个vm实例,就在app.vue创建。
3、用来编写待渲染的模板结构,index.html中预留一个el区域,让main.js把app.vue渲染到了index.html预留的区域中
13-2-5、index.html配置
<!DOCTYPE html>
<html lang="">
  <head>
    <meta charset="utf-8">
    <!--  针对IE浏览器的一个特殊配置,让IE浏览器以最高的渲染级别渲染页面-->
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <!-- 开启移动端的理想视口 -->
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <!-- 配置页签图标 ,<%= BASE_URL %>:路径的写法:相当于 ./  -->
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <!-- 配置网页的标题。找到package.json的name属性 -->
    <title><%= htmlWebpackPlugin.options.title %></title>
  </head>
  <body>
    <!-- noscript:如果浏览器不支持js,这里面的元素就会被渲染 -->
    <noscript>
      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <!-- 容器 -->
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>

13-2-6、改变入口文件
1、 https://cli.vuejs.org/zh/config/#pages
2、 创建一个vue.config.js文件,然后再去官网的配置参考里面找到pages,选择代码
粘贴到创建的文件里面,去掉多余的
  module.exports = {
    pages: {
      index: {
        // page 的入口
        entry: 'src/index/peiqi.js',
     
      }
    }
  }
13-2-7、关掉语法检查
1、 https://cli.vuejs.org/zh/config/#pages
2、 创建一个vue.config.js文件,然后再去官网的配置参考里面找到lintOnSave,把配置拿过 lintOnSave:false 
   module.exports = {
    pages: {
      index: {
        // page 的入口
        entry: 'src/index/peiqi.js',
     
      }
    },
    lintOnSave:false
  }
13-2-8、vue单文件
1、组件化开发:根据封装的思想,把页面上可重用的UI结构封装为组件,方便项目的开发和维    护
2、vue是一个支持组件化开发的前端框架,组件的后缀名是.vue,
3、vue文件的组成部分,组件是对UI结构的复用
     1、template:组件的模板结构,
     2、script:组件的javascript行为
     3、style:组件的样式
4、export  defalut:默认导出,有引入就要有导出
5、组件中的data必须定义为函数类型,返回一个对象,因为每个组件都有属于自己的数据,避免数组重复,所以每个组件的数据通过data这个函数得到自己的数据
6、可以安装Vetur可以使用快捷键<得到vue文件的模板
7、组件必须只能由一个根节点。

13-3、组件之间的关系

1、组件在被封装好以后,没有引入关系的时候,是相互独立的,不存在父子关系
2、如果在使用组件的时候,根据彼此的嵌套关系,形成了父子关系、兄弟关系
3、父组件引入子组件的步骤
    1:使用import语法导入需要的组件
    2、使用compoents节点注册组件  键值一样可以简写
    3、以标签的形式使用刚才注册的组件
4、通过components注册是私有子组件
5、使用 <右键可以直接生成vue组件
6、组件分为私有组件和全局祖册组件
13-3-1、使用@代替src的方法
1、下载一个插件 Path Autocomplete
2、选择 齿轮-设置-打开设置
3、复制代码到最前面
   //导入文件时是否携带文件的扩展名
   "path-autocomplete.extensionOnImport":true,
   //配置@的路径提示
   "path-autocomplete.pathMappings":{
   "@":"${folder}/src"
   }
4、然后就可以引入了
import Self from '@/components/Self.vue'
(案例:two)

13-3-2、注册全局组件
1、注册一次,全局都可以使用
2、在vue项目的main.js入口文件中,通过Vue.component()方法,可以注册全局组件
    
  // 导入需要全局注册的组件
  import Self from '@/components/Self.vue'

  // 参数1:字符串格式,表示组件的”注册名称“
  // 参数2:需要背全局注册的那个组件

   Vue.component('MySelf',Self)
3、注册完以后就可以通过注册名称直接引入这个组件了

main.js文件

// 引入残缺版的vue,残缺版的vue不能自己渲染页面
// 只有完整版的vue能够渲染,
// vue由两部分组成:核心(生命周期、路由)+模板解析器
// 节省空间,残缺版去掉了模板解析器,通过render函数
// 去渲染页面

import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

// 导入需要全局注册的组件
import Self from '@/components/Self.vue'


// 参数1:字符串格式,表示组件的”注册名称“
// 参数2:需要背全局注册的那个组件
Vue.component('MySelf',Self)




new Vue({
   render:h=> h(App)

}).$mount('#app')

14、组件之间的通信(分享数据)

14-1、props(父组件给子组件传递数据->自定义属性)

1、props:是组件的自定义属性,在封装通用组件的时候,合理的使用props可以极大地提高组件的复用性(自我总结:就是说每个组件(vue文件)都有这个属性。但如果这些文件的初始值不一样,而且被映射到一个文件上面,就可以使用props)
(被引入的组件是子组件,引入组件的是父组件。在父组件里面把属性传给子组件,子组件使用   props接收)

2、props的语法格式:
   props:["自定义属性1","自定义属性2"]
   是一个数组,和data等平级关系,里面的属性用字符串包裹
   
3、子组件使用props接收父组件的步骤:(父组件是App.vue,子组件是father.vue)
    1、在引入组件的时候,在父组件里面使用子组件的标签对里传值   
    <Father :count="count"></Father>
    2、在子组件里面接收
     props:['count']
    3、使用这个属性   
 注意:子组件的这个属性改变不会影响到父组件的属性发生改变
    
4、 props可以传递具体的值,也可以传变量,是自定义属性。在传值的时候为当前组件指定初始值。这个自定义属性是封装者自己定义的。可以极大地提高组件的复用性

5、props传递变量的三种方式
  1、传递的是字符串
      <Son count="9"></Son>
  2、传递的是数值
     <Son :count="9" ></Son>
  3、传递的是变量
    <Son :count="count" ></Son>
 因为 :代表v-bind,里面是js语句,相当于count=9

6、props的数据,可以直接在模板结构中被使用,并且,子组件不要修改props接收到的值,会报错。

7、vue规定,组件中封装的自定义属性是只读的,程序员不能直接修改props的值,不然会直接报错。

8、可以把props值传给data
    num:this.count

9、props的default值,来定义属性的默认值。写成对象的形式,一般是在组件没有传递该属性时候,则默认值生效
      props:{
        count:{
            default:5
        }
    }

10、props的type值类型,设置属性的类型,传入的值必须是该类型
       props:{
        count:{
            default:5type:Number
        }
    }

11、props的required:必填,要求必须传递该属性的值
       props:{
        count:{
            default:5,
            required:true
        }
    }

12、简答类型复制的是值,复杂类型的是引用地址。

14-2、子组件向父组件传递数据(使用自定义事件)

1、子组件可以通过自定义时间向父组件传递数据。
   1、在子组件中自定义一个事件,使用 this.$emit('btn-click', item)的语法,emit指代发射事件,btn-click是我们自定义的事件名,item是子组件中的数据。 注意::vue官方推荐你始终使用 kebab-case格式的事件名。
   
   2、在父组件中使用v-on监听在子组件中我们自定义的数组,并为其在父组件中定义一个接收监听的事件
   
   3、在父组件中接收数据

    <button @click="add">点我传送数据给父组件App</button>

     methods: {
        add(){
            this.$emit('num',this.message)
        }
    },

    <Son @num="add"></Son>
 methods: {
    add(n){

      console.log(n);
    }
  },

14-3、兄弟组件之间的数据共享(EventBus:全局事件总线,可实现任意组件通信)

1、创建一个eventBus.js模块,并向外共享一个Vue的实例对象

2、在数据发送方,调用bus.$emit("事件名称",要发送的数据)方法触发自定义事件

3、在数据接收方,调用bus.$on("事件名称",事件处理函数)方法注册一个自定义事件
1、自定义一个eventBus.js
   import Vue from 'vue'

  export default new Vue()
2、在数据发送方,引入这个文件。再定义一个事件,然后通过这个事件调用bus.$emit("事件名称",要发送的数据)方法触发自定义事件
   <template>
  <div>

    <!-- 数据发送方 -->
   <button @click="send">发送数据</button>
  </div>
</template>

<script>
import bus from '@/components/eventBus.js'
export default {
    data() {
        return {
            msg:"好好学习,天天向上"
        }
    },
 
    methods: {
        send(){
            bus.$emit("num",this.msg)
        }
     
    },

}
</script>
3、在数据接收方,使用create生命周期函数调用bus.$on("事件名称",事件处理函数)方法注册一个自定义事件
<template>
  <div>
    <div>接收方{{str}}</div>
  </div>
</template>

<script>
import bus from '@/components/eventBus.js'
export default {
    data() {
        return {
            str:""
        }
    },
    created() {
        bus.$on("num",val=>{
            this.str=val
        })
    },

}
</script>

14-4、vuex(状态管理)

vuex官方解释
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 + 库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
14-4-1 、vuex的概念
1、概念:专门在vue中实现集中式状态(数据)管理的一个插件 Vue.use()。对vue应用中的多个组件的共享状态进行集中式状态 数据  管理(读/写)。也是一种组件间通信的方式,且适合用于任意组件间的通信
14-4-2、什么时候使用vuex(共享)
1、多个组件依赖于统一状态(多个组件需要用到同一个数据,就可以让vuex来管理这个护具)

2、来自不同组件的行为需要变更同一状态(需要改变这同一个数据)

14-4-3、vuex工作原理图

在这里插入图片描述

每一个 Vuex 应用的核心就是 store,里面又包括:

1. State(状态):用于数据的存储(对象类型数据),是store中唯一数据源

2. Actions(行为):类似于mutation,用于提交mutation来改变状态,而不直接变更状态,可以包含任意异步事件

3. Mutations(转变):类似函数,改变state数据的唯一途径,且不能用于处理异步事件。

Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的事件类型 (type)和一个回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方

4. Getter(数据加工):如vue中的计算属性一样,基于state数据的二次包装,常用于数据的筛选和多个数据的相关计算

5. Module:类似于命名空间,用于项目中将各个模块的状态分开定义和操作,便于维护

由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块。

vuex工作原理说明

1、,actions、mutations,state

2、actions:动作、行为

3、mutations:维修,加工

4、state:状态,把数据交给vuex的state对象进行保管。

5、dispatch:分发,派遣。
14-4-4、vuex的搭建环境
1、安装vuex
   npm i vuex@3
   
2、在main.js里面设置vue.use(Vuex)
  import Vuex  from 'vuex'
  //就可以配置store项
 Vue.use(Vuex)

3、在src里面创建一个文件夹store,创建一个index.js
   用于创建vuex中最核心的store,
   在js文件里面创建三个对象   
  
  // 准备actions-用于响应组件中的动作
   const actions={}

  // 准备mutations-用于操作数据
  const mutations={}

   // 准备dtate-用于存储数据
  const  state={}
 
  然后创建一个store对象来管理这几个对象
  因为store是通过Vuex的Store创建的。所以我们要引入Vuex
  // 引入Vuex创建store
  import Vuex from 'vuex'

  然后创建store
   const store =new Vuex.Store({
    actions,
    mutations,
    state

})

// 向外暴露,因为主要是要的是store,所以暴露的是store
export default store


  
4、在main.js引入这个store,所有的组件对象能看见这个 store
   // 引入store
  import store from './store'
  //配置
  
new Vue({
  el:"#app",
  store,

  //render:渲染
  
//  h是一个函数,
// h去渲染元素

render:h=> h(App)

})

5、报错,现这样错误的原因是在创建store实例之前,没有调Vue.use(Vuex),但这个时候会有人说,已经调用了Vue.use(Vuex),还是会出现这样的错误。究其原因是因为Vue脚手架会解析文件中的所有import语句,然后把所有import语句按照编写代码的顺序全都汇总在最上方,之后才会解析文件中的其它代码,这就会使得Vue.use(Vuex)在store实例之后执行。
 import引入最高级,会优先执行import的js,所以先创建store实例会报错。

6、解决:在store.js,引入Vue后,在使用Vue.use(Vuex)
  import Vue from 'vue'
   Vue.use(Vuex)

620227月,vue3成为了默认版本, 现在npm i vue,安装的就是vue3,vue3成为默认版本,vuex也更新到了44只能在vue3中使用,现在使用npm i vuex安装的是4版本
我们项目使用的是2版本,使用vuex4就会报错。所以我们要安装3版本

store.js

// 引入Vuex创建store
import Vuex from 'vuex'


// 准备actions-用于响应组件中的动作
const actions={}

// 准备mutations-用于操作数据
const mutations={}

// 准备dtate-用于存储数据
const  state={}


// 创建store
// 因为store是管理

// const store =new Vuex.Store({
//     actions,
//     mutations,
//     state

// })

// 向外暴露,因为主要是要的是store
// export default store


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

})

main.js

// 引入残缺版的vue,残缺版的vue不能自己渲染页面
// 只有完整版的vue能够渲染,
// vue由两部分组成:核心(生命周期、路由)+模板解析器
// 节省空间,残缺版去掉了模板解析器,通过render函数
// 去渲染页面
import Vue from 'vue'
import App from './App.vue'
// 引入vuex
import Vuex from 'vuex'
// 引入store
import store from './store'

Vue.use(Vuex)

Vue.config.productionTip = false



new Vue({
  store,
  render:h=> h(App)
}).$mount('#app')

解决报错

store.js

// 引入Vuex创建store
import Vuex from 'vuex'
// 引入vue
import Vue from 'vue'


// 准备actions-用于响应组件中的动作
const actions={}

// 准备mutations-用于操作数据
const mutations={}

// 准备dtate-用于存储数据
const  state={}

Vue.use(Vuex)

// 创建store
// 因为store是管理

// const store =new Vuex.Store({
//     actions,
//     mutations,
//     state

// })

// 向外暴露,因为主要是要的是store
// export default store


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

})

main.js

// 引入残缺版的vue,残缺版的vue不能自己渲染页面
// 只有完整版的vue能够渲染,
// vue由两部分组成:核心(生命周期、路由)+模板解析器
// 节省空间,残缺版去掉了模板解析器,通过render函数
// 去渲染页面
import Vue from 'vue'
import App from './App.vue'
// 引入vuex
import Vuex from 'vuex'
// 引入store
import store from './store'



Vue.config.productionTip = false



new Vue({
  el:"#app",
  store,

  //render:渲染
  
//  h是一个函数,
// h去渲染元素

render:h=> h(App)





})
// .$mount('#app')

14-4-5、vuex工作流程

1、state管理数据,所以把想让vuex管理的数据保存到state中。

2、当我们想改变state保管的数据的状态的时候。就使用action保管的函数方法,使用dispatch方法将执行那个函数方法拍给action执行。
  this.$store.dispatch('函数方法',参与运算的数据参数)
    this.$store.dispatch('Jia',this.num)

3、action函数里面响应这个方法,所以需要在action里面定义这个函数方法,使用对象格式,也可以简写,这个函数方法可以接收到两个参数,一个是context,这个上下文对象就包括了commit方法,可以
  函数方法名(context,n){
  函数体
  }
  
4、action的这个函数方法可以接收到两个参数,一个是context,这个上下文对象就包括了commit方法,可以使用这个context对象调用commit方法,由 actions 提交一个 mutation。记住!!!这个函数方法名改为大写,为了区分mutation和action写的函数
 函数方法名(context,n){
  context.commit(‘函数方法名’,n){5、在mutation里面准备定义这个函数,也有两个参数,a是state,b是传递的参数数据
  就可以通过a.数据和数据进行处理
  mutation=大写的函数方法名(a,b){
       
          
      }
      }
6、展示state里面的数据的写法
  $store.state.数据名
     
7、actions决定业务逻辑是否调用mutation的函数,包括发送异步请求

8、如果函数不需要什么业务逻辑,可以直接mutation对接,直接调用commit调用mutation里面的函数
  this.$store.commit('大写的函数方法名',参数数据)

     
9、组件中地区vuex的数据,$stroe.state.sum 
     
10、组件中使用actions的数据:$store.dispatch('action中的方法名',数据)
      $store.commit('mutation中的方法名',数据)
     (如果没有网络请求或起亚业务逻辑,可以绕过actions,直接commit)

工作流程

https://blog.csdn.net/JHY97/article/details/124045131?ops_request_misc=&request_id=&biz_id=102&utm_term=vuex%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86%E5%9B%BE&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-1-124045131.142^v87^control_2,239^v2^insert_chatgpt&spm=1018.2226.3001.4187
14-4-6、vuex的getters
1、getters其实就是store的计算属性,对state里面的状态进行过滤处理,用法与组件自身的计算属性一模一样。

2、使用,和actions一样定义,然后和计算属性一样在里面定义方法,方法返回一个数值
  const getters={
    
  }
  然后把getters放到store里面
  export default newVVuex.Store({
  actions,
  mutations,
  state,
  getters
  }
  
3、当state中的数据需要经过加工后再使用时,可以使用getters加工  
14-4-7、vuex的mapState
1、mapState是什么
   用于帮助我们映射state中的数据为计算属性
   因为我们可以在组件的计算属性的方法里面,直接返回state里面的数据,而
   this.$store.state.数据 。如果返回state数据的函数方法太多,太麻    烦。mapState可以帮我们生成这种函数方法
  
   
   使用:先在需要映射的组件里面引入这个组件
    import  {mapState} from 'vuex'

   在computed里面使用mapState生成函数方法
   mapState({方法名:"state里面的数据"})

  如果方法过多,我们可以解构的形式
  ...mapState({方法名:"state里面的数据",方法名2:"state里面的数据2"})
   
  简写,把数据名用字符串括起来放在数组里面
    ...mapState(['数据名1''数据名2'])
   
2、mapGetters方法:用于帮助我们映射getters中的数据为计算属性
   和mapstate用法一致


3、mapMutations方法:用于帮助我们生成与mutations对话的方法,即:包含          $store.commit(xxx)的函数
    和mapstate用法一致的话,会出错。传入的是鼠标事件,因为生成的函数没有参数,而mutation定义的参数里面是有value的,而默认是有一个事件对象,所以这个事件对象会在被作为value值传给mutations的value值
    ...mapMutations({方法名1:"mutations里面的方法1",方法名2:"mutations里面的方法12"})
   
  解决方法:在调用的时候,把这个数传过去
  方法名1(n)
  
4、mapActions方法:用于帮助我们生成与actions对话的方法,即:包含                $store.dispatch(xxx)函数
    和mapMuatations用法一致
     
14-4-8、vuex模块化
1、因为会有不用的功能,比如订单模块,用户模块,可以把这些模块分成不同的对象,统一配置actions,state,mutations,
  const 对象1={
   const actions={}const state={}const mutations={}const getters={}
  }const 对象2={
   const actions={}const state={}const mutations={}const getters={}
  }


2、暴露store的时候,去掉actions,state,mutations,使用modules放置定义的对象,这里的a,b是别名
  export default new Vuex.Store({
   modules:{
   a:配置对象1b:配置对象2
   }

})

3、简写,这样写的话,可以输出this.$store看一下
   export default new Vuex.Store({
   modules:{
   配置对象1,
   配置对象2
   }

})


4、计算属性使用的时候,这是将mapstate方法将a,b生成计算属性,将store的state下面的a,b作为计算属性的方法,返回这两个数据,因为它们是两个对象,所以可以使用a.属性名,b.属性名。
 ...mapState(['a','b'])

5、直接从配置对象里面拿到数据,表示从别名1里面拿去数据,但是只是这样肯定会报错,还需要给配置对象设置一个namespaced:true属性
 ...mapState(‘别名1,['a','b'])

 const 对象1={
    namespaced:true,
   const actions={}const state={}const mutations={}const getters={}
  }6、方法也是一样
     ...mapMutations(‘别名1’,{方法名1:"mutations里面的方法1",方法名2:"mutations里面的方法1"})
     就可以调用这个方法,记得传参

7、如果是直接调用commit方法,要找到具体的是哪一个配置对象的方法
  this.$store.commit('配置别名/配置方法名',传入的参数)

8、获取配置对象的getters的方法
  this.$store.getters['配置别名/配置方法名']

9、如果是直接调用dispatch方法,要找到具体的是哪一个配置对象的方法
  this.$store.dispatch('配置别名/配置方法名',传入的参数)

10、模块化语法减少代码耦合,更好维护代码。让各种数据分类更加明确
14-4-9、vuex的详细用法
1、开启命名空间后,组件中读取state的数据
    1—自己直接读取
      this/$store.state.配置名.数据名
    2-借助mapState读取
      ...mapState('配置别名'['数据名1''数据名2'])
      
2、开启命名空间后,组件中读取getters的数据
    1—自己直接读取
       this.$store.getters['配置别名/配置方法名']
    2-借助mapGetters读取
      ...mapGetters('配置别名'['方法名1''方法名2'])

      
3、开启命名空间后,组件中调用dispatch
    1—自己直接dispatch
       this.$store.dispatch('配置别名/配置方法名',传入的参数)
    2-借助mapActions生成方法,方法调用需要传递参数
      ...mapActions('配置别名'['方法名1''方法名2'])
      或者
      ...mapActions('配置别名'{组件定义的方法名:'配置对象的方法名'})
      
4、开启命名空间后,组件中调用commit
    1—自己直接commit
       this.$store.commit('配置别名/配置方法名',传入的参数)
    2-借助mapMutations生成方法,方法调用需要传递参数
      ...mapMutations('配置别名'['方法名1''方法名2'])
      或者
      ...mapMutations('配置别名'{组件定义的方法名:'配置对象的方法名'})    

14-5、消息订阅

个人省略

15、组件之间的样式冲突问题

1、默认情况下,写在.vue组件中的样式会全局生效,因此会很容易造成多个组件之间的样式冲突问题。原因是因为
    1、单页面应用程序,多有组件的DOM结构,都是基于唯一的index.html进行呈现
    2、每个组件中的样式,都会影响整个index.html中的DOM元素
2、解决样式冲突问题
   在style里面加上scoped,vue会自动为该组件的元素生成私有属性
   
     <style lang="less" scoped>
     </style>
3、如果想在父组件里面改变子组件的样式,而其他引入了子组件的不会发生改变,那么,不仅要在该父组件定义scoped,还要在改变的样式前面加上一个前缀,这样的话就会变成一个后代选择器。
     /deep/ div{
    width: 200px;
    height: 200px;
    background-color: blue;
   }

16、生命周期

16-1、简单介绍

官网:每一个vue实例从创建到销毁的过程,就是这个vue实例的生命周期。在这个过程中,他经历了从开始创建、初始化数据、编译模板、挂载Dom、渲染→更新→渲染、卸载等一系列过程。
1、生命周期:是一个vue组件从  创建 ->运行->销毁  的整个阶段,强调的是一个时间段

2、生命周期函数:是vue提供的内置函数,会伴随着组件的生命周期,自动按次序执行

3、生命周期强调的是时间段,生命周期函数强调的是时间点

4、组件创建阶段: beforeCreate(组件还没开始创建之前)、created(内存里面创建好,还没被渲染)、beforeMount(将要渲染的时候)、mounted(渲染好的时候,刚好看到组件的时候)

5、组件运行阶段:beforeupdate(组件更新前)、updated

6、created最重要,因为我们异步请求数据就是在这个周期函数里面

1、创建期间的生命周期函数:
  1)beforeCreate:实例刚在内存中被创建出来
    此时还未初始化完毕data和methods
  2)created:实例已经在内存中创建完毕
    此时 data和methods已经创建完毕 但此时还未开始编译模板
  3)beforeMount:此时已经完成模板的编译
    但是还未挂载到页面中
  4)mounted:此时已将编译好的模板挂载到了页面指定的容器中显示
2、运行期间的生命周期函数:
  1)beforeUpdate:状态更新之前执行此函数
    此时data中的状态值是最新的 但界面上显示的数据还是旧的
    因为此时还未开始重新渲染DOM节点
  2)updated:实例更新完毕之后调用此函数
    此时data中的状态值和界面上显示的数据都已完成了更新 界面已被重新渲染好了
3、销毁期间的生命周期函数:
  1)beforeDestroy:实例销毁前调用
    在这 实例仍然完全可用
  2)destroyed:Vue实例销毁后调用
    调用后 Vue实例指示的所有东西都会解除绑定 所有的事件监听器会被移除 所有的子实例     也会被销毁

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ISgI4l6Z-1686141820663)(C:\Users\Direct\Desktop\常用前端框架及工具\拓展学习资料\生命周期函数.png)]

组件创建阶段


<template>
  <div id="app">
      <h3></h3>

  </div>
</template>

<script>


export default {
  name: "App",
  components: {
  
  },
  data() {
    return {
      count:5
    }
  },
  beforeCreate(){
    console.log(this.count);
  },
  created(){
      //异步请求数据使用该函数
    console.log(this.count);
    console.log(document.querySelector("h3"));
      // data和methods已经创建完毕 但此时还未开始编译模板,所以不能操作dom
    // document.querySelector("h3").innerText="Hello"
  },
  beforeMount(){
      //还没有渲染
    // document.querySelector("h3").innerText="Hello"
  },
  mounted(){
      //编译好已经挂载到页面上,最早操作dom元素
    document.querySelector("h3").innerText="Hello"
  }

};
</script>

组件运行阶段

<template>
  <div id="app">

      <h3>生命周期</h3>
      <h4>{{ count }}</h4>
      <button @click="count++">count++</button>

  </div>
</template>

<script>


export default {
  name: "App",
  components: {
  
  },
  data() {
    return {
      count:5
    }
  },
 
  beforeUpdate(){
    // data中的状态值是最新的 但界面上显示的数据还是旧的
    console.log(this.count);
    console.log( "h4的值"+document.querySelector("h4").innerHTML);
  },
  updated() {
    //数据和模板结构完成同步
      //为了操作大哦最新的dom结构,必须写到upDate声明周期函数
    console.log(this.count);
    console.log( "h4的值"+document.querySelector("h4").innerHTML);
  },

};

</script>

17、ref引用(Vue用来操作DOM元素的)

1、ref是用来辅助开发者在不依赖jquery的情况下,获取DOM元素或组件的引用

2、每个vue的组件实例上,都包含一个$refs对象,里面存储着对应的DOM元素激活组件的引用。默认情况下,组件的$refs指向一个空对象

3、给一个Dom元素定义一个ref属性,然后取一个名字,就可以通过$refs.名字对这个DOM元素进行操作

4、如果想要使用ref引用页面上的组件实例,还可以给组件使用ref。然后通过this.$refs.组件的ref的名字.fn(),就可以运行该组件的方法
<!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 src="../lib/vue.js"></script>
</head>

<body>
    <div id="app">
        <span ref="s">使用ref操作dom元素</span>
        <button @click="fn">输出this</button>

    </div>
    <script>
        const vm = new Vue({
    el:"#app",
    methods: {
        fn(){
            console.log(this.$refs.s);
        }
    },

        })

    </script>
</body>

</html>

根组件引用Ref组件过后,通过ref属性使用他的方法

<template>
  <div id="app">
 <button @click="fn">App:按我输出组件r的方法</button>
 <br>
 <br>

    <Ref ref="r"></Ref>

  </div>

</template>

<script>
import Ref from '@/view/Ref.vue'

export default {
  name: "App",
  components: {
 Ref
  
  },
  data() {
    return {
     
    }
  },
  methods:{
    fn(){
      this.$refs.r.fn()
    }
  }

};

</script>

17-1、this.$nextTick()方法

1、this.$nextTick()方法主要是用在随数据改变而改变的dom应用场景中,
   vue中数据和dom渲染由于是异步的,
   所以,要让dom结构随数据改变这样的操作都应该放进this.$nextTick()的回调函数中。

2、created()中使用的方法时,dom还没有渲染,如果此时在该钩子函数中进行dom赋值数据(或者其它dom操作)时无异于徒劳,
所以,此时this.$nextTick()就会被大量使用,而与created()对应的是mounted()的钩子函数则是在dom完全渲染后才开始渲染数据,
所以在mounted()中操作dom基本不会存在渲染问题。

3、就是数据发生改变了,但是页面还没有反应过来,就需要使用这个this.$nextTick(),等待数据渲染后应用在dom里面,需要dom重新渲染的时候就需要用到这个方法。把回调推迟到下一个DOM更新周期之后执行
this.$nextTick(()=>{
  函数体
})

18、动态组件

1、动态组件指的是动态切换组件的显示和隐藏

2、vue提供了一个内置的<component>组件,专门用来实现动态组件的渲染,作用:组件的占位符,is属性的值:表示要渲染的组件的名字。而且,is属性的值,应该是组件在components节点下的注册名称。也就是必须是引入的组件,注册过后,才能使用。

3、可以把这个<component>看做一个组件的占位符。但是直接使用会报错,未知的元素错误,因为这个元素还没有被定义。

4、在component组件里面使用is属性选择要渲染的组件
  
   <component is="Hello"></component>
   <component is="Hi"></component>
    
5、对于飘红线显示错误的情况,使用v-bind绑定is属性,然后使用反引号括起来组件名字
       <component :is="`Hello`"></component>
       <component :is="`Hi`"></component>
    
6、组件的切换,会不断的创建和销毁,字符串的值用单引号括起来,is属性绑定str
 <div id="app">
    <nav>App根组件
 
      <button @click="str = 'Hello'">切换为Hello组件</button>
   
      <button @click="str = 'Hi'">切换为Hi组件</button>     
    </nav>
    <component :is="str"></component>
    
  </div>

7、使用keep-alive保持让组件隐藏的时候不会被销毁,直接使用<keep-alive></keep-alive>标签将切换的动态组件括起来
      <div id="app">
        <nav>App根组件
 
         <button @click="str = 'Hello'">切换为Hello组件</button>
   
        <button @click="str = 'Hi'">切换为Hi组件</button>           
    </nav>
          
      <keep-alive>
         <component :is="str"></component>
      </keep-alive>
    
  </div>
    
8、打开vue调试工具,在切换inactive表示被缓存了,没有被销毁

9、如果想在组件被缓存的时候做什么,在组件被激活的时候做什么,有对应的生命周期函数
      当组件被缓存的时候,会自动触发组件的deactivated生命周期函数
      当组件被激活的时候,会自动触发组件的activated生命周期函数
created() {
    console.log("Hello组件已经创建");
  },
  destroyed() {
    console.log("Hello组件已经销毁");
  },
  deactivated() {
    console.log("Hello组件缓存");
  },
  activated (){
    console.log("Hello组件激活");
  }

10、组件第一次被创建的时候,会激活created,也会激活activated生命周期,组件被激活的时候只会触发activated,不再触发created。
    
11、keep-alive的include属性:在component会把component包裹的组件都缓存起来。可以使用include属性用来指定。至于名称相匹配的组件会被缓存,多个组件名之间使用,就是说用它来指定哪些组件需要被缓存,多个组件名之间用,分隔。不被包含在里面的都不会被缓存,记住这个include指定的组件名是在组件里面的定义的name属性
    //只有Hello组件可以被缓存
  <keep-alive include="Hello">
      <component :is="str"></component>
    </keep-alive>
    
12、exclude属性:默认哪些组件不会被缓存,不能和include同时使用   
    
13、在组件提供了name属性后,组件的名称就是name属性的值。如果在“声明组件”的时候,没有为组件指定name名称,则组件的名称默认就是“注册时候的名称”,
    
14、注册名称以标签的形式使用,把注册好的组件渲染和使用到页面结构中
    name名称为了在调试工具里面看到组件名称,以及结合《keep-alive》实现组件缓存功能

Hello子组件

<template>
  <div>
  
    Hello组件

  </div>
</template>

<script>
export default {
  created() {
    console.log("Hello组件已经创建");
  },
  destroyed() {
    console.log("Hello组件已经销毁");
  },
  deactivated() {
    console.log("Hello组件缓存");
  },
  activated (){
    console.log("Hello组件激活");
  }
}
</script>

<style lang="less" scoped>
div{
  width: 100%;
  height: 400px;
   position: absolute;
  top:58px;
  background-color: rgb(248, 245, 49);
}


</style>

父组件

<template>
  <div id="app">
    <nav>App根组件
 
      <button @click="str = 'Hello'">切换为Hello组件</button>
   
      <button @click="str = 'Hi'">切换为Hi组件</button>
      
     
    
      
    </nav>
    <component :is="str"></component>
    
  </div>


</template>

<script>

import Hello from './components/Hello.vue'
import Hi from './components/Hi.vue'

export default {
  name: "App",
  components: {
    Hello,
    Hi,



  },
  data() {
    return {
      str:""

    }
  }

};

</script>
<style lang="less" scoped>
* {
  padding: 0;
  margin: 0;
}

nav {
  width: 100%;
  height: 50px;
  background-color: #eee;
}
</style>

Hi子组件

<template>
    <div>

        Hi组件

    </div>
</template>
  
<script>
export default {

}
</script>
  
<style lang="less" scoped>
div {
    width: 100%;
    height: 400px;



    background-color: orange;
}
</style>

19、插槽

19-1、插槽的基本用法

1、插槽(slot)是vue为组件的封装这提供的能力,允许开发者在封装组件的时候,把不确定、希望由用户指定的部分定义为插槽。

2、简单来说就是在使用注册过的组件时,如果要往那个标签对里面插入什么元素,但是不会显示出来,就可以往子组件里面写一个插槽,这样这个组件标签对里面写的东西就可以显示出来了

3、用户使用什么,插槽就渲染什么

4、vue官方规定,每一个插槽都应该有一个name名称,如果没有定义会有一个default的默认名称
     <slot name="p"></slot>

5、如果插槽定义了name属性,那么我们要使用 v-slot:插槽的名字,去指定是哪一个插槽,而且要注意的是,这个被指定要使用插槽的元素要使用template包裹。
  例如:根组件引用了Hello组件,在标签对里面使用了一个p标签,然后,在hello组件里面定义了一个name属性为p的插槽,然后根组件就需要将这个p标签用template包裹起来。然后在template里面定义v-slot:p
  Hello组件
   <slot name="p"></slot>
  App组件
   <Hello>
        <template v-slot:p>
          <p >你好,这是插槽内容</p>
        </template>
      </Hello>
    
6、注意:
    1、如果要把内容填充到指定名称的插槽中,需要使用v-slot
    2、v-slot值只能用在template身上,而且渲染的页面是看不见这个template元素的,只会看见被渲染的元素
    3、v-slot:插槽的名字
    4、插槽指令v-slot的简写形式是: #,就是一个#号
1、比如,在APP根组件里面引入了Hello组件,然后声明注册后以标签对的形式使用,在里面定义了一个p标签,但不会显示内容,只有在Hello里面定义一个slot插槽,接收到这个p标签,才能显示内容
普通的定义插槽

根组件

<template>
  <div id="app">
    <nav>App根组件
 
     
      <Hello>
        <p>你好,这是插槽内容</p>
      </Hello>
    
      
    </nav>
 
    
  </div>


</template>

<script>

import Hello from './components/Hello.vue'
import Hi from './components/Hi.vue'

export default {
  name: "App",
  components: {
    Hello,
    Hi,



  },
  data() {
    return {
      str:""

    }
  }

};

</script>
<style lang="less" scoped>
* {
  padding: 0;
  margin: 0;
}

nav {
  width: 100%;
  height: 50px;
  background-color: #eee;
}
</style>

Hello组件

<template>
  <div>
  
    Hello组件
    <slot></slot>

  </div>
</template>

<script>
export default {
 
}
</script>

<style lang="less" scoped>
div{
  width: 100%;
  height: 400px;
   position: absolute;
  top:58px;
  background-color: rgb(248, 245, 49);
}


</style>

有名字的插槽

Hello组件

<template>
  <div>
  
    Hello组件
    <slot name="p"></slot>

  </div>
</template>

<script>
export default {
 
}
</script>

<style lang="less" scoped>
div{
  width: 100%;
  height: 400px;
   position: absolute;
  top:58px;
  background-color: rgb(248, 245, 49);
}


</style>

App组件

<template>
  <div id="app">
    <nav>App根组件
 
     
      <Hello>
        <template v-slot:p>
          <p >你好,这是插槽内容</p>
        </template>
      </Hello>
    
      
    </nav>
 
    
  </div>


</template>

<script>

import Hello from './components/Hello.vue'
import Hi from './components/Hi.vue'

export default {
  name: "App",
  components: {
    Hello,
    Hi,



  },
  data() {
    return {
      str:""

    }
  }

};

</script>
<style lang="less" scoped>
* {
  padding: 0;
  margin: 0;
}

nav {
  width: 100%;
  height: 50px;
  background-color: #eee;
}
</style>

19-2、具名插槽和作用域插槽

1、带名字的插槽就是具名插槽,

2、slot插槽可以定义属性,在template里面用v-slot接收
  子组件:
      <slot name="p" msg="好好学习,天天向上"></slot>
  根组件
        <Hello>
        <template #p="obj">
          <p >你好,这是插槽内容</p>
          {{ obj }}
        </template>
      </Hello>

3、在封装组件时,为预留的slot提供属性对象的值,这种用法叫做作用域插槽

4、这种作用域插槽也是具名插槽,用v-slot接收,v-slot可以用#来进行简写。数据对象可以用=来接收。对象的名字用scope定义
       <Hello>
        <template #p="scope">
          <p >你好,这是插槽内容</p>
          {{ obj }}
        </template>
      </Hello>

5、作用域插槽可以使用解构接收数据对象
写法:可以直接使用这个msg
  #p="{msg}"

也可以通过scope直接接收所有数据
  #p="scope"
  {{scope.msg}}

20、自定义指令

1、vue官方提供了v-text、v-for、v-model等常用的指令,还允许开发者自定义指令

2、vue的自定义指令分为两类:
    1)、私有自定义指令
    2)、全局自定义指令

3、私有自定义指令:
     在每个vue组件中,可以在directives(自定义指令节点)节点下声明私有自定义指令
     就在组件实例里面定义,和methods、data等平级,这个指令是对象形式,定义的指令      也是对象形式
      // 这是自定义指令的节点
   directives:{
    // 定义一个color的指令,指向一个配置对象
      color:{
    // 当这个指令绑定到元素的时候,会马上触发bind函数
    // bind函数会接收一个el参数,这个el参数就是绑定的dom元素
       bind(el){
      console.log(el);
         }
      }
  }

4、例如,定义一个color的指令,可以在绑定元素的时候,把元素的背景颜色渲染为绿色
     // 这是自定义指令的节点
  directives:{
    // 定义一个color的指令,指向一个配置对象
  color:{
    // 当这个指令绑定到元素的时候,会马上触发bind函数
    // bind函数会接收一个el参数,这个el参数就是绑定的dom元素
    // 可以利用这个el参数对Dom对象做操作,将背景颜色渲染为绿色
    bind(el){
      el.style.backgroundColor="green"
    }

  }

  }

5、这种定义私有指令的时候,不用使用v-开头来定义,但是使用的时候必须使用v-开头,例如
  <div class="box" v-color>
  这是一个红色的盒子
  </div>
 
6、自定义指令接收实际参数,会有一个binding对象。binding对象里面有一个value,可以接收到传入的数据,而且,,如果传入的数据时字符串格式的话,要记得给字符串加上单引号。不带引号传入的是一个变量。写法:
     <div class="box" v-color="'yellow'">
      这是带有参数的自定义指令
    </div>

  定义私有指令的写法
  directives:{ 
  color:{
    bind(el,binding){
      el.style.backgroundColor=binding.value
    }
  }
  }

7、expreion:是一个表达式,就是这个=后面的表达式。代表用户写的东西。

8、bind函数的缺点,只会在第一次绑定元素的时候执行,而且只执行一次,而如果页面数据更新,是无法改变的,二update函数会在每次Dom更新的时候调用。
   updated() {
      console.log(this.str);
    },
 
9、凡是使用到了这个指令的元素,都会触发这个update函数

10、update只会在数据更新的时候生效,不会在第一次的时候生效


11、函数简写:
   如果bind和update函数中的逻辑完全相同,则对象格式的自定义指令可以写成函数格式:
    color(el,binding){  
      el.style.backgroundColor=binding.value
  
}
 
12、私有自定义指令只能在当前定义的组件使用,不能在其他组件使用

13、全局共享的自定义指令需要在main.js  通过“vue.directive()”进行声明
    像定义全局过滤器一样,以对象形式写两个函数方法
    Vue.directive('c',{
      binding(el,binding){
      el.style.color=binding.value
      },
        update(){
      el.style.color=binding.value
      }
})

14、如果逻辑一样,可以以函数形式写代码
    Vue.directive('c',function(el,binding){
    el.style.color=binding.value

})

main.js

// 引入残缺版的vue,残缺版的vue不能自己渲染页面
// 只有完整版的vue能够渲染,
// vue由两部分组成:核心(生命周期、路由)+模板解析器
// 节省空间,残缺版去掉了模板解析器,通过render函数
// 去渲染页面
import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

Vue.directive('c',function(el,binding){
el.style.color=binding.value

})


new Vue({
  el:"#app",
 render:h=> h(App)

})
// .$mount('#app')

bind对象和它的缺点

<template>
    <div id="app">
      <nav>App根组件
   
      <div class="box" v-bgc>
         这是一个红色的盒子
      </div>
  
      <!-- 直接传入一个颜色值 -->
    <div class="box" v-color="'yellow'">
    这是带有参数的自定义指令
      </div>
  
  
      <button @click="str='yellowgreen'">改变str的颜色</button>
    <div class="box" v-color="str">
      这是使用变量的带参数的自定义指令
      ,看清楚bind函数只执行一次
        </div>
        <div class="box" v-color="str">
          这是使用变量的带参数的自定义指令
          ,看update的更新
            </div>
        
      </nav>
   
      
    </div>
  
  
  </template>
  
  <script>
  
  import Hello from './components/Hello.vue'
  import Hi from './components/Hi.vue'
  
  export default {
    name: "App",
    components: {
      Hello,
      Hi,
  
  
  
    },
    data() {
      return {
        str:"pink"
  
      }
    },
    // 这是自定义指令的节点
    directives:{
      // 定义一个color的指令,指向一个配置对象
   bgc:{
      // 当这个指令绑定到元素的时候,会马上触发bind函数
      // bind函数会接收一个el参数,这个el参数就是绑定的dom元素
      // 可以利用这个el参数对Dom对象做操作
      bind(el){
        el.style.backgroundColor="green"
      }
  
    },
    color:{
      bind(el,binding){
     
        el.style.backgroundColor=binding.value
      }
    }
  
    },
    //生命周期函数,看见这个str已经改变,但是因为bind而不会更新页面
    updated() {
      console.log(this.str);
    },
  
  };
  
  </script>
  <style lang="less" scoped>
  * {
    padding: 0;
    margin: 0;
  }
  
  nav {
    width: 100%;
    height: 50px;
    background-color: #eee;
  }
  .box{
    width: 200px;
    height: 200px;
    background-color: red;
  }
  </style>

update的优点

<template>
  <div id="app">
    <nav>App根组件
 
   

    <button @click="str='yellowgreen'">改变str的颜色</button>
  
      <div class="box" v-color="str">
        这是使用变量的带参数的自定义指令
        ,看update的更新
          </div>
      
    </nav>
 
    
  </div>


</template>

<script>

import Hello from './components/Hello.vue'
import Hi from './components/Hi.vue'

export default {
  name: "App",
  components: {
    Hello,
    Hi,



  },
  data() {
    return {
      str:"pink"

    }
  },
  // 这是自定义指令的节点
  directives:{
    // 定义一个color的指令,指向一个配置对象
 bgc:{
    // 当这个指令绑定到元素的时候,会马上触发bind函数
    // bind函数会接收一个el参数,这个el参数就是绑定的dom元素
    // 可以利用这个el参数对Dom对象做操作
    bind(el){
      el.style.backgroundColor="green"
    },
  },
  color:{
    bind(el,binding){
   
      el.style.backgroundColor=binding.value
    },
    update(el,binding){
      el.style.backgroundColor=binding.value
}
}

  },
  updated() {
    console.log(this.str);
  },

};

</script>
<style lang="less" scoped>
* {
  padding: 0;
  margin: 0;
}

nav {
  width: 100%;
  height: 50px;
  background-color: #eee;
}
.box{
  width: 200px;
  height: 200px;
  background-color: red;
}
</style>

21、路由

21-1、理解路由

1、路由就是对应关系,route

2、路由器:router

3、路由就是一组key-value的对应关系,多个路由需要经过路由器的管理

4、路由就是为了实现单页面应用。

5、项目由导航区和展示区组成
1、路由指的就是Hash地址与组件之间的关系

2、#号代表锚链接,不会导致页面的刷新,会导致历史记录的变化

3、普通的超链接:<a href="路径"></a> 是跳转到不同的页面

   锚点:<a href="位置"></a> 可以在同一个页面中不同的位置间跳转
   
4、建立锚点目标,只需要给目标元素增加 id 或者 name 即可

   锚的名称可以是任何你喜欢的名字

   可以使用 id 属性来替代 name 属性,命名锚同样有效,推荐使用 id
   
5、#往后都叫做hash地址,   

锚链接

<!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>#锚链接</title>
    <style>
        div{
            height: 800px;
        }
    #d1{
        background-color:red ;
    }
    #d2{
        background-color:rgb(26, 224, 108) ;
    }
    #d3{
        background-color:rgb(22, 56, 230) ;
    }
    #d4{
        background-color:rgb(231, 65, 143) ;
    }
    nav{
        position: fixed;
        top: 0;
        left:0;
    }

    </style>
</head>
<body>
  <nav>
    <a href="#d1">d1</a>
    <a href="#d2">d2</a>
    <a href="#d3">d3</a>
    <a href="#d4">d4</a>
  </nav> 
  <div id="d1"></div> 
  <div id="d2"></div> 
  <div id="d3"></div> 
  <div id="d4"></div> 
</body>
</html>

21-2、路由的工作方式

1、点击页面上的路由连接后,url地址栏上的hash值发生改变,前端路由监听到了hash地址的变化,然后把当前hash地址对应的组件渲染到浏览器中

2、在html页面中对应关系: a#d1  <->  div#d1

3、在vue中路由:path:"#/d1",compoment:#d1

21-3、简单的路由实现

1、window.onhashchange监听页面的hash值变化

2、location.hash.substring(2)截取本地的hash值的字符串2位后的字符串

3、使用动态组件决定显示那个页面,is属性绑定一个动态组件,切换组件
<template>
  <div id="app">
    <nav>

      <h2>App根组件</h2>
      <a href="#/Home">首页</a> |
      <a href="#/Login">登录</a>  |
      <a href="#/Regist">注册</a>  |
    </nav>
    <component :is="cname"></component>


  </div>
</template>

<script>
import { nanoid } from 'nanoid'
import Hello from './components/Hello.vue'
import Home from './components/Home.vue'
import Login from './components/Login.vue'
import Regist from './components/Regist.vue'

export default {
  name: "App",
  components: {
    Hello,
    Home,
    Login,
    Regist




  },
  data() {
    return {
      n: 5,
      id: 0,
      cname:'Home'

    }
  },
  created() {
    this.id = nanoid()
   
    window.οnhashchange=()=>{
      // console.log("hash值发生变化",location.hash);
  //  console.log( location.hash.substring(2));
  this.cname=location.hash.substring(2)
      
    }


  },


};

</script>
<style lang="less" scoped>
* {
  padding: 0;
  margin: 0;
}

nav {
  width: 100%;
  height: 50px;

  background-color: #eee;
}

.box {
  width: 200px;
  height: 200px;
  background-color: red;
}
</style>

21-4、vue-router的使用

1202227日以后,vue-router的默认版本,为4版本,4版本适用于vue3
  3版本的vue-router适用2版本的vue。我们直接安装vue-router就会报错,   出现。
   Found:vue @2.。。。
   peer vue@“^3.0..
   所以一定要安装正确版本
   
2、cmd安装vue--router的版本,指定3版本
  npm i vue-router@3
  
3、vue-router是一个插件库,需要use。
  import VueRouter from 'vue-router'
   Vue.use(VueRouter)

4、创建一个router文件夹,用于创建整个应用的路由器,创建一个index.js
  文件。
  1)、引入路由vue-router
   import  VueRouter from 'vur-router'
  2)、创建路由器
   const router=new VueRouter({ }) 
  3)、配置路径routes,path,表示显示的url路径的hash地址,component     表示那个hash地址显示对应的组件 
    // 创建一个路由器
  const router=new VueRouter({
    routes: [
        {
            path:'/hello',
            component:Hello
        }
    ]
}) 
   4)、因为配置的component是组件上,所以我们还需要引入组件
   import Login from '@/components/Login.vue'
   import Regist from '@/components/Regist.vue'
   
   5)、暴露这个路由器
    export default router

   6)、简写
   export default new VueRouter({
    routes: [
        {
            path:'/hello',
            component:Hello
        }
    ]
}) 
   
   7)、在main.js里面引入路由器
     import  router from '@/router'
  
   8)、使用router-link标签跳转路径,这个标签实际上就是a标签
       <router-link to="/login">Login</router-link>
 
   9)、这个时候页面没有展示出来。需要向slot插槽一样,给要显示组件的地方需要用到 router-view占位。组件一个router-view标签,表示router显示的页面展示在这个位置
       <router-view></router-view>
   
10、这些定义了hash路径的组件都叫做路由组件

router的index.html

// 用于创建整个应用的路由器
import  VueRouter from 'vue-router'
// 引入组件
import Login from '@/components/Login.vue'
import Regist from '@/components/Regist.vue'

// 创建一个路由器
export default new VueRouter({
    routes: [
        {
            path:'/login',
            component:Login
        },
        {
            path:'/regist',
            component:Regist
        }
    ]
   

}) 

main.js

// 引入残缺版的vue,残缺版的vue不能自己渲染页面
// 只有完整版的vue能够渲染,
// vue由两部分组成:核心(生命周期、路由)+模板解析器
// 节省空间,残缺版去掉了模板解析器,通过render函数
// 去渲染页面
import Vue from 'vue'
import App from './App.vue'

import VueRouter from 'vue-router'
import  router from '@/router'

Vue.use(VueRouter)

Vue.config.productionTip = false



new Vue({
  el:"#app",
  router:router,
  render:h=> h(App)

})
// .$mount('#app')

App引入router

<template>
  <div id="app">
    <nav>

      <h2>App根组件</h2>
    
    </nav>
 
    <div class="box">
      <router-link to="/login" >Login</router-link>
    </div>
    <div class="txt">
      <router-link to="/regist" >Regist</router-link>

    </div>

    <div class="tt">

      <router-view></router-view>
    </div>

  </div>
</template>

<script>

export default {
  name: "App",
  components: {


  },
  data() {
    return {
      

    }
  },
 
};

</script>
<style lang="less" scoped>
* {
  padding: 0;
  margin: 0;
}

nav {
  width: 100%;
  height: 50px;

  background-color: #eee;
}

.box {
  width: 100px;
  height: 30px;
  text-align: center;
  border: 1px solid #a1a0a0;

}
.txt{
  width: 100px;
  height: 30px;
  text-align: center;
  border: 1px solid #a1a0a0;

  border-top: 0;
}

</style>

21-5、路由注意点

1、配置了hash路径的组件都叫做路由组件

2、这些路由组件一般放在pages文件夹下面。

3、$route:这个组件的路由信息,每个路由组件都有。 route是单个路由,存放           当前路径信息,携带的参数

4、$router:整个应用的路由器。只有一个路由器。 管理整个路由系统,里面保            存所有的路径信息,能够实现路由的跳转

5、通过切换。“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载

21-6、嵌套路由

1、一级路由,定义在routes对象中,是routes对象的属性,被定义为一级路      由。

2、多级路由是一级路由的孩子。使用routes的children属性定义,children是一个数组,里面可以配置多个对象,对象的写法和以及路由是一样的。是被定义在一级路由为代表的路由组件里面的路由组件。children里面不用添加 /
        {
            path:'/regist',
            component:Regist,
            children:[
                {
                    path:'home',
                    component:Home
                }
            ]
        },

3、使用的时候,记得to属性加上完整路由。
   <router-link to="/regist/home">Home组件</router-link>

21-7、路由传参

1、路由跳转的时候可以传递参数,而跳转到的那个路由组件可以接收到值传递的这个参数值。
  <router-link to="/regist/home/hello?id=01"> {{ item.name }}</router-link>
 
2、每个路由组件都有一个this.$route对象,有关于这个路由组件的很多多信息。
    <li>学生学号{{ $route.query.id}}</li>

3、动态展示传递过去的参数,使用属性绑定v-bind绑定to属性,让字符串变成动态表达式,然后使用模板语法用 ` `括起来,把name属性=对象的id属性,使用${}。让这句话变成模板字符串。这是跳转路由携带query参数的to的字符串写法

<router-link

:to="`/regist/home/hello?id=${item.id}&name=${item.name}`"
> 
     {{ item.name }}
    
    </router-link>
                    
4、跳转路由并携带query参数to的对象写法,path代表路径,表示你跳转的路由组件的地址,还有query参数,query是一个对象,里面装的就是要携带的参数,这样写的好处是简单明了
      <router-link :to="{
                        path:'/regist/home/hello',
                        query:{
                            id:item.id,
                            name:item.name
                        }
                     }">
                    {{ item.name  }}
                    </router-link>

21-8、命名路由

1、name是什么呢?name 是配置路由时给 path 取的别名,方便使用。但要注意的是 “地址栏显示的路径始终是 path 的值”.可以简化代码,就在routes设置里面添加一个name属性。
      {
            name:'login',
            path:'/login',
            component:Login,
            
        
        },
            
2、这个name属性可以简化代码。比如说路径跳转的时候我们使用path进行路径跳转。
  //在routes设置跳转的 路由的name属性
      {
            name:'login',
            path:'/login',
            component:Login,
            
        
        },
     //路由跳转:通过name属性跳转
             <router-link :to="{
                        name:'hello',
                        query:{
                            id:item.id,
                            name:item.name
                        }
                     }">
                    {{ item.name  }}
                    </router-link>

            

21-9、params参数

字符串写法
1、在跳转前的组件通过to属性使用/传递参数,但是要在路径的path属性里面去声明这是接收参数。通过占位符来声明,跳转到的路由组件接收参数的写法也不一样。
     //传递固定格式的数据的写法
                    <router-link :to="`/regist/home/hello/007/王老五`">
                        {{ item.name}}
                    </router-link>

     //传递动态数据的写法
       
                    <router-link :to="`/regist/home/hello/${item.id}/${item.name}`">
                        {{ item.name}}
                    </router-link>

2、在路由组件的路由设置里面通过占位符声明接收的参数。这些数据就会放在params对象里面。
    path:'hello/:id/:name',
     
3、路由组件接收的参数的方式,通过params对象去接收:
         <li>学生学号{{ $route.params.id}}</li>  

对象写法
  必须把path换成name就可以了。不能使用path,
  然后query对象的参数换成params对象设置传递的数据参数
  
  
4、解决路由组件接收参数写死的情况,路由的props配置:,
  在路由里面设置,使用rpops参数去接收,
    
  1//第一种写法,值为对象,
   {
                            name:'hello',
                            path:'hello/:id/:name',
                           component:Hello,
   //    props的第一种写法,值为对象,该对象
     // 所有key-value都以props形式传递给这个hello组件
                           props:{a:1,str:"小红"}
                        }
  路由组件通过props接收这个两个变量的值
     props:['a','str'],
  组件可以直接使用这两个数据,但是,这是写死的数据,不推荐使用
        <li>{{ a}}</li>
        <li>{{ str }}</li>
  

   2//第二种写法,值为布尔值,
    // props的第二种写法,值为布尔值,若布尔值为真
    // 就会把该路由收到的所有的params参数以props
    // 的形式转给该路由组件
       {
         name:'hello',
         path:'hello/:id/:name',
         component:Hello,              
       // props的第二种写法,值为布尔值,若布尔值为真
       // 就会把该路由收到的所有的params参数以props
        / 的形式转给该路由组件
         props:true
                    }  
   
   引用路由组件的字符串写法
  <router-link :to="`/regist/home/hello/${item.id}/${item.name}`">
         {{ item.name}}
   </router-link> 
    路由组件通过props接收这个两个变量的值
       props:['id','name'],
    组件可以直接使用这两个数据,但是,这是写死的数据,不推荐使用
        <li>学生学号{{ id}}</li>
        <li>学生姓名{{ name}}</li>

    对象写法
      <!-- 6、路由跳转携带params 参数,引用路由的组件对象写法写法-->
        
               <router-link :to="{
                name:'hello',
                params:{
                    id:item.id,
                    name:item.name
                }
               }">
                        {{ item.name}}
                    </router-link>
   
   4)如果把路径删了,想转成query参数。通过props接收,不能使用props接收的数据,会告诉你属性未定义,只能通过query对象使用数据
  引用路由的组件设置跳转路由
     <!-- 7、跳转路由由query 接收props传递的数据 -->
                    <router-link :to="{
                     name:'hello',
                     query:{
                        id:item.id,
                        name:item.name
                     }
                   
                    }"> {{ item.name }}</router-link>
     路由组件接收传递的参数的写法
        <li>学生学号{{ $route.query.id}}</li>
        <li>学生姓名{{ $route.query.name}}</li>
   路由设置
        {
            name:'hello',
           // path:'hello/:id/:name',
            path:'hello',
            component:Hello,
          // 如果把路径删了,想转成query参数。通过props接收。
          props:true
   }
   
    5)props的第三种方法:值为函数。
         // props的第三种写法:值为函数,
               //值为函数的第一种写法
         // props(){
         //  这是写死的用法。这个props传递的数据既不在params
          //里面,也不在query里面
          //   return {id:11,name:"小兰"}
          /  }
             
    
     //值为函数的第二种写法:
     // 传入route对象,就可以使用query里面的参数传递数据 
      props($route){
       // 这是写死的用法。这个props传递的数据既不在params
       //    里面,也不再query里面
       
      return {
          id:$route.query.id,
          name:$route.query.name}
  }

    //值为函数的第三种写法:
       // 解构query对象,使用里面的数据
        props({query}){
       return {id:query.id,name:query.name}
     }

       //值为函数的第四种写法:
        // 再解构赋值
         props({query:{id,name}}){
            return {id,name}
          }

5、props的作用:让路由组件更方便的收到参数,既适用于params也适用于  query

21-10、params和query的不同

  在Vue路由中,query和params都是用于向路由添加附加信息的方式。query通常用于传递查询字符串,而params则用于传递路由参数。query通过URL中的“?”传递附加信息,而params则在路由路径中传递,例如“/users/:id”。params可以使用$router.push()来更改,而query则使用$router.replace()。查询参数和路由参数在Vue的$route对象中都可以使用,但是路由参数更适用于表示唯一标识符,例如用户ID,而查询参数更适合用于分页或搜索过滤器等用途

21-11、路由跳转的两种导航方式

1、声明式导航,
        在浏览器中,点击链接实现导航的方式,叫做声明式导航。

        例如:普通网页中点击a链接,vue项目中点击<router-link>都属于声明式导航
        
2、 编程式导航
      在浏览器中,调用API方法实现导航的方式,叫做编程式导航。

      例如:普通网页中调用location.href 跳转到新页面的方式,都属于编程式导航

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


   

21-12、router-link的replace属性

1、路径以push模式增加为历史记录,一条条路径重叠,不破坏任何一条路径。最新的页面在最上面

2、replace属性直接替换当前路径页面,不会生成页面记录,写法,直接添加replace属性,在router-link里面。
   
<router-link 

    replace
    
    to="/regist/home">Home组件

</router-link>

3、总结:replace属性的作用
   1)、作用:控制路由跳转时操作浏览器历史记录的模式
   
   2)、浏览器的历史记录有两种写入方式,分别为push和replace,push是追加历史记录,replace是替换当前记录,路由默认为push
   
   3)、开启replace模式的方法:在router-link里面添加replace属性。

21-13、编程式导航

1、在浏览器中,调用API方法实现导航的方式,叫做编程式导航。

      例如:普通网页中调用location.href 跳转到新页面的方式,都属于编程式导航

2、使用路由器:$router的api方法跳转
   pushShow(m){
            this.$router.push({
                    name:'hello',
                     query:{
                        id:m.id,
                        name:m.name
                     }

            })

        },
        replaceShow(m){
            this.$router.replace({
                    name:'hello',
                     query:{
                        id:m.id,
                        name:m.name
                     }

            })

        }
3、使用路由器方法的前进后退:
      this.$router.back
      this.$router.forward

4、编程式导航的作用:不借助router-link实现路由跳转,让路由跳转更灵活

21-14、缓存路由组件

1、通过keep-alive:让不展示的组件保持挂载,不被销毁

2、是在路由router-link里面指定,include属性指定哪个组件被挂载

3、缓存多个组件的写法
  :include="['组件1','组件2']"

21-15、路由组件相关的生命周期

1、activated:激活时触发

2、deactivated:失活时触发

3、路由组件所独有的两个钩子,用于捕获路由组件的激活状态

21-16、全局路由守卫

1、路由守卫的作用:对路由进行权限控制,分类:全局守卫、独享守卫、组件内守卫。如果想实现登录注册后才能看见网站首页,就需要添加路由守卫

2、第一步就是在路由组件里面选择不直接暴露router。而是直接定义一个router对象,

const router=new VueRouter({
routes:[
{
 
}
]
})

//全局前置路由守卫,路由切换之前会调用
//初始化的时候调用
router.BeforeEach((to,from.next)=>{

})

3、to和from是去的路由组件和前一个路由组件,里面是路由组件的相关信息。next()表示放行,如果不放行,是不会执行到下一步,

4、所以前置路由守卫一般是用来判断,是否登录注册过,才可以跳转页面,然后可以路由跳转到其他页面,to.name也可以
   if(to.path=='/login' || to.path =='/regist'){
       if(这里和数据库的会员数据做比对,一致的话就可以放行){
           next()
       }
       else
           {
               console.log("不能成功登录")
           }
       
   }

5、meta:提供容器配置特殊的数据。称作路由原信息,配置程序员自己想配置的信息,可以用来作为验证路由守卫的信息。也可以用来做网页的title信息。
  const router=new VueRouter({
routes:[
{
    name:'hello',
    path:'/hello',
    component:Hello,
    meta:{isNext:true}
 
}
]
})

6、使用meta信息判定权限:
//判断是否需要鉴权
     if(to.meta.isNext){
       if(这里和数据库的会员数据做比对,一致的话就可以放行){
           next()
             
       }
       else
           {
               console.log("不能成功登录")
           }
       
   }

7、全局后置路由守卫:初始化之后调用,每次切换之后调用,没有next,不需要放行。
    router.afterEach((to,from)=>{
        document.title=to.meta.title  || '网站'
    })

21-17、独享路由守卫

1、某一个路由单独想用的守卫。

2、想设置独享路由守卫,在设置路由里面设置beforeEnter。表示进入这个路由之前。进行规则的对比,只对该路由组件做测试。
   routes:[
{
    name:'hello',
    path:'/hello',
    component:Hello,
    meta:{isNext:true},
    beforeEnter((to,from,next)=>{
   
   })
 
}
]

3、独享守卫只有前置没有后置,可以喝全局后置守卫搭配使用。

21-18、组件内路由守卫

1、beforeRouteEnter,路由组件使用的函数,通过路由规则,进入该组件时被调用。
 beforeRouteEnter(to,from.next){
 
 }
 
2、beforeRouteleave,路由组件使用的函数,通过路由规则,离开该组件时被调用。
     beforeRouteleave(to,from.next){
 
 }

21-19、history模式与hash模式

1、hash值最大的特点:hash值不会因为http请求作为路径数据发给服务器,是程序员自己设置的路径

2、可以通过在路由器里面设置mode属性,选择路由是history模式还是hash模式,默认是hash模式
  const router=new VueRouter({
   mode:'history',
   routes:[
   
   ]
  })
  
3、开启history模式后不会再有hash值。需要新开页签

4、hash兼容性好,history兼容性较差。

5、npm的中间件:connect-history-api-fallback专门解决history模式404的问题。

6、总结;
  hash模式:
    1、地址中带着#号,不美观
    2、如果以后将地址通过第三方手机app分享,如果app校验严格,则地址会被标记不合法
    3、兼容性较好
    
  history模式:
     1、地址干净、美观
     2、兼容性和hash模式相比略差
     3、应用部署上线时需要后端人员支持,解决刷新页面服务器404问题

21-20、路由重定向

暂时省略

21-21、路由懒加载

暂时省略

22、minxin:混入

22-1-1、什么是混入

1、mixins(混入),官方的描述是一种分发 Vue 组件中可复用功能的非常灵活的方式 mixins 是一个 js 对象,它可以包含我们组件中 script 项中的任意功能选项,如:data、components、methods、created、computed 等等。我们只要将公用的功能以对象的方式传入 mixins 选项中,当组件使用 mixins 对象时所有 mixins 对象的选项都将被混入该组件本身的选项中来,这样就可以提高代码的重用性,并易于后期的代码维护

22-1-2、怎么使用mixin

1、当我们存在多个组件中的数据或者功能很相近时,我们就可以利用 mixins 将公共部分提取出来,通过 mixins 封装函数,组件调用他们是不会改变函数作用域外部的。
2、一样的方法我们把它提取出来放在一个公共的地方

22-1-3、如何创建mixin

1、在 src 目录下创建一个 mixins 文件夹,在文件夹下新建一个myMixins,js 文件。 因为 mixins 是一个 js 对象,所以应该以对象的形式  来定义 myMixins,在对象中可以和vue 组件一样来定义 data、components、methods、created、computed   等属性,  并通过 export 导出该对象。export表示分别暴露,如果需要在组件里面使用该混合,需要在该组件引入,因为是分别暴露,所以,需要以对象的形式暴露:
      import {mixin} from '../mixin'
   然后,所以我们使用mixins配置项去接收,因为有多个的mixin,所以数组形式接收,只有一个混合也这样写
       mixins:[mixin]
2、组件有的东西以组件为主,组件没有的以混合为主
3、生命周期mountend都会实现
4、功能:就是可以把多个组件共用的配置提取成一个混入对象

22-2、案例

export  const mixin={
    methods:{
        fn(){
          
            this.n++
        }
    }
}

<template>
  <div>
    {{ n }}
<button @click="fn">按我+1</button>

  </div>
</template>

<script>
import {mixin} from '../mixin'
export default {
    data(){
        return{
            n:2

        }
    },
    mixins:[mixin]
   

}
</script>


22-3、定义全局混合

1、在main.js里面引入混合,然后使用Vue.mixin()就可以使用该混合里面的方法了
  import {mix}  from './mixin'
  Vue.mixin(mix)

23、过渡与动画

1、过渡与动画的作用:在插入或移除DOM元素的时候,在核实的时候给元素添加样式类名

2、有进入的样式和离开的样式

3、进入
   v-enter:进入的起点
   v-enter-active:进入过程中
   v-enter-to:进入的终点

4、离开
   v-leave:离开的起点
   v-leave-active:离开过程中
   v-leve-to:离开的终点
   
5、进入的终点就是离开的起点

6、使用transtion包裹想要过渡的元素,

23-1、动画

23-2-1、Vue动画的理解
1、设置来回切换的效果,vue用transtion包裹有动画效果的元素,设置进入的时候的的类名:v-enter-active,设置离开的时候的类名:v-leave-active。

2、这个transtion还可以起名字,
    <transition name="tr">
    <nav v-show="flag">动画效果</nav>

  </transition>

3、如果给transtion起了名字,那么,v-enter-active就改成 transtion的名字-enter-active

4、设置一开始就是动画效果,就给transtion设置一个appear属性,设置属性值为true,但是要通过v-bind进行绑定。不然的话是一个布尔值
       <transition name="tr" :appear="true">
      <nav v-show="flag">动画效果</nav>
  
    </transition>
 
5、transtion在vue解析的时候没有被解析,是给vue 设置动画的,动态给元素添加样式的动画效果

<template>
  <div>
    <button @click="flag=!flag">显示/隐藏</button>
  <transition>
    <nav v-show="flag">动画效果</nav>

  </transition>

  </div>
</template>

<script>
export default {
    data(){
        return {
            flag:1
        }
    }

}
</script>

<style lang="less" scoped>
nav{
    width: 1000px;
    height: 100px;
    background-color: aquamarine;
  
}
.v-enter-active{
  animation: trans 1s linear reverse;
}
.v-leave-active{
  animation: trans 1s linear ;
}

@keyframes trans {
  from{
    transform: translateX(0%);

  }
  to{
    transform: translateX(-100%);
  }
}
</style>

23-2、过渡

1、vue提供了transtion的封装插件,在下列情形中,可以给任何元素和组件添加enter/leave过渡
  1、v-if
  2、v-show
  3、动态组件
  4、组件根节点

过渡的类名
1、v-enter:进入的起点,在外边的盒子以左边坐标为开始,向右从-100%的位置开始

2、v-enter-to:进入的终点,在里边的盒子以左边的坐标未开始,向左从0%的位置转

3、v-leave:离开的起点,0%的位置开始转移

4、v-leave-to:离开的终点,转移到-100%的位置

5、给元素设置transtion。同样位置的一样的类名设置可以放在一起
<template>
    <div>
      <button @click="flag=!flag">显示/隐藏</button>
    <transition >
      <nav v-show="flag">动画效果</nav>
  
    </transition>
  
    </div>
  </template>
  
  <script>
  export default {
      data(){
          return {
              flag:1
          }
      }
  
  }
  </script>
  
  <style lang="less" scoped>
  nav{
      width: 1000px;
      height: 100px;
      transition: 2s linear;
      background-color: aquamarine;
    
  }
  .v-enter{
    transform: translateX(-100%);
  }
  .v-enter-to{
    transform: translateX(0%);
  }
 
  .v-leave{
    transform: translateX(0%);
  }
  .v-leave-to{
    transform: translateX(-100%);
  }
 

  </style>
22-2-1、多个元素过渡
1、 can only be used on a single element. Use <transition-group> for lists.
   transtion只能有一个元素,多个元素可以使用transtio-group

2、transtion-group的元素必须有唯一的key值
 
  <transition-group name="tr" appear>
    <nav v-if="flag" key="one">1</nav>
    <nav v-if="flag" key="two">2</nav>
  </transition-group>

22-3、动画库(使用第三方库写动画样式)

1、搜素npm.js官网,https://www.npmjs.com/

2、在官网里面搜索animate.css,点击第一个
   https://www.npmjs.com/package/animate.css
   
3、选择animate.style
  https://animate.style/
  
4、根据animate上面的步骤进行使用:
   1、安装,停止该项目,安装animate
   2、引入样式库
     import 'animate.css'
   3、复制粘贴类名,放在name属性上
     animate__animated animate__bounce
   4、给这个transtion指定进入和离开的类名,复制类名 字符串里面是自己喜欢的动画效果
  
  进入的类名:enter-active-class=””
  
   离开的类名:leave-active-class=””
   <transition-group
   name="animate__animated animate__bounce" 
   appear
   enter-active-class="animate__wobble"
   leave-active-class="animate__zoomOutDown"

   
   >
    <nav v-if="flag" key="one">1</nav>
    <nav v-if="flag" key="two">2</nav>
  </transition-group>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1nGHkSMl-1686141820666)(C:\Users\Direct\Desktop\常用前端框架及工具\拓展学习资料\7-animate使用.PNG)]

23、Vue原理

23-1、MVVM

1、Vue坐着参考mvvm模型,m(model)代表模型,v(view)代表视图,模板,vm,视图模型,vue实例对象

23-2、数据代理

1、是否被枚举指的是是否可以被遍历出来

2、数据代理:通过一个对象代理对另一个对象中属性的操作

3、重点:Object.defineProperty,

24、配置代理

1、跨域,也就是违背了同源策略:主机名、协议名、端口号必须一致

2、解决跨域的方法
      1.cors,后端人员添加特殊的响应头,
      2、jsonp,通过script标签,前端后端一起设置,只能设置get方法
      3、代理服务器,服务器和服务器之间传递数据不用ajax请求,使用的是http协议,而代理服务器的端口号和客户端的端口号这些是一致的,满足同源策略
服务器之间不受同源策略的影响

3、代理服务器的几种方式:
     1、nginx
     2、借助vue-cli
     
4、vue脚手架设置代理服务器的方式,在vue.config.js里面设置
    1、打开vuejs的官网,选择vue2文档,点击配置参考,选择devServer。procy,
    2、按照参考文档设置
    3、lintOnSave:false  //关闭语法检查
    
    //举例
    module.exports = {
    devServer: {
    //这里是告诉代理服务器等会在哪个服务器请求数据,在这里就开启了一个
    //代理服务器,所以设置的是请求数据的服务器端口
      proxy: 'http://localhost:4000'
    }
  }
  
    4、在axios请求数据的时候改变端口号,只改变自己的端口号,不改变请求路径。然后重启项目,比如服务器端口号是4000,但我们设置了代理服务器,axios请求的路径本来是4000,但我们改成本机端口号80880
    axios.get('http://localhost:4000')
   改:axios.get('http://localhost:8080')
 
 5、两个小问题:
      1、只能配置一个代理服务器
      2、没办法控制走不走代理,因为如果文件的public有路径文件,会优先获取public里面的。
      
6、解决以上两个问题的方法,看官网
  第二种方法
 module.exports = {
  devServer: {
    proxy: {
        //第一个代理
        ///api叫做请求前缀,如果请求前缀是api就走代理服务器
      '/api': {
          //target是服务器的路径
        target: '<url>',
          //用于支持websocket:
        ws: true,
          //true表示隐瞒自己的端口号,
          //用于控制请求头中的host值
        changeOrigin: true
      },
        //第二个代理
      '/foo': {
        target: '<other_url>'
      }
    }
  }
}

 注意:在这里因为设置了请求前缀,我们的axios也要作出相应改变,在端口号的后面添加请求前缀,其他的不会改变
  axios.get('http://localhost:8080/api')
 

  但是因为axios请求的路径会完整的带给服务器去请求数据,所以我们要去设置一个配置。
  //重写路径,它是一个对象格式,里面是key、value形式,表示所有以api开头的路径将被替换为空字符串
  pathRewrite:{'/api':''}

7、第二种方法的优点:可以配置多个代理,且可以灵活的控制请求是否走代理
             缺点:配置稍微繁琐,请求资源时必须添加前缀

25、axios

25-1、项目中使用

1、在vue里面使用axios需要先安装axios,

  npm   i axios  -S

2、在组件中引入

  import  axios from  'axios'

3、调用方法

​     axios.get()

25-2、axios拦截器

暂时省略

26、nanoid

1、使用nanoid可以给数据生成id,

2、
  先引入
  import {nanoid} from 'nanoid'
  
  后使用,直接使用
  this.id=nanoid()

27、Vue UI组件库

27-1、移动端组件库

1、Vant
2、Cube  UI
3、Mint  UI

27-2、PC端常用UI组件库

1、element  UI
2、IView  UI

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

docker cgroub

docker 的资源管理 1、cpu的资源控制 一&#xff1a;设置cpu的资源上限 cd /sys/fs/cgroup/cpu/docker/ cpu 的占用量达到100% cpu 设置一半50% 2. 设置cpu资源占用比&#xff08;设置多个容器才有用&#xff09; docker run -itd --name c3 --cpu-shares 512 centos:7 do…

springboot+mybatis实现删除(二)

一&#xff0c;XML映射文件和动态SQL XML映射文件的名称与Mapper接口名称一致&#xff0c;并且将XML映射文件和Mapper接口放置在相同包下&#xff08;同包同名&#xff09;&#xff0c;右键创建目录/分隔&#xff0c;例&#xff1a;com/baidu/crm XML映射文件的namespace属性为…

工程训练 -江苏海洋大学-mooc-最终答案

这不点赞评论一下嘛&#xff1f;&#xff1f;&#xff1f;呜呜呜 判断题&#xff08;共217道&#xff09; 1.舂实模样周围及砂箱边或狭窄部分的型砂&#xff0c;通常采用砂舂的平头端舂砂。 2.造型时&#xff0c;分型面上通常使用的是面砂&#xff0c;覆盖模样的则使用背砂。 3…

【微服务】springboot整合swagger多种模式使用详解

目录 一、前言 1.1 编写API文档 1.2 使用一些在线调试工具 1.3 postman 1.4 swagger 二、swagger简介 2.1 背景 2.2 swagger优缺点 2.2.1 swagger优点 2.2.2 swagger缺点 2.2.3 swagger使用场景 三、swagger常用的几种整合模式 3.1 swagger2 3.2 knife4j 3.2.1 k…

《设计模式》之命令模式

文章目录 1、定义2、动机3、类结构4、优缺点5、总结6、代码实现(C) 1、定义 将一个请求封装为一个对象&#xff0c;从而使你可用不同的请求对客户端进行参数化&#xff1b;对请求排队或记录请求日志&#xff0c;以及支持可撤销的操作。 2、动机 在软件构建过程中&#xff0c…

虚拟内存和物理内存:概念、原理和应用

目录 概述1. 概念2. 原理3. 设计寻位原理4. 应用场景结论 概述 当你使用计算机时&#xff0c;内存是一个非常重要的资源。它用于存储正在运行的程序和数据&#xff0c;确保系统的正常运行。在计算机系统中&#xff0c;存在着虚拟内存和物理内存的概念&#xff0c;它们共同协作…

Docker Gitlab Container Registry配置

文章目录 前言一、Registry是什么二、步骤配置gitlab.rb文件修改docker-compose.yaml文档验证推送镜像 总结 前言 找了很多资料包括官网1都没有发现比较清楚的配置registry的方法&#xff0c;自己摸索了半天发现其实通过简单设置就能够配置好Container Registry。 之所以在题…

【Docker】3.Docker Registry

文章目录 Docker RegistryDocker Registry CommandImage Command NginxNginx System installNginx Config Container CommandCreate My DegistryBusyBox腾讯云镜像仓库搭建 Docker Registry 镜像仓库负责存储、管理、分发镜像&#xff0c;并且提供了登录认证的能力&#xff0c…

LeetCode_二叉树_DFS_中等_129.求根节点到叶节点数字之和

目录 1.题目2.思路3.代码实现&#xff08;Java&#xff09; 1.题目 给你一个二叉树的根节点 root &#xff0c;树中每个节点都存放有一个 0 到 9 之间的数字。 每条从根节点到叶节点的路径都代表一个数字&#xff1a;例如&#xff0c;从根节点到叶节点的路径 1 -> 2 -> …

【计算机网络】IP 地址处理函数

目录 1.struct sockaddr_in的结构 2.一般我们写的结构 3.常见的“点分十进制” 到 ” uint32_t 的转化接口 3.1. inet_aton 和 inet_ntoa &#xff08;ipv4&#xff09; 3.2. inet_pton 和 inet_ntop (ipv4 和 ipv6&#xff09; 3.3. inet_addr 和 inet_network 3…

人工智能-深度学习-科研神器推荐

根据知乎问题 有没有什么可以节省大量时间的 Deep Learning 效率神器&#xff1f; 的回答&#xff0c;筛选整理出一些深度学习科研神器。包括参数优化、数据可视化、模型部署蒸馏剪枝等。收录到 人工智能-深度学习-科研神器推荐https://​www.webhub123.com/#/home/detail?p4O…

vue监听缓存数据(localStorage)

方法&#xff1a;可以重写localStorage的setItem方法&#xff0c;当调用setItem方法设置新值的时候&#xff0c;会new Event(‘setItemEvent’) 用window.dispatchEvent()这个方法来派发一个事件&#xff0c;让window去监听 以下demo实现的是 一个页面获取诗句 然后将获取的数据…

【P51 】JMeter 聚合报告(Aggregate Report)

文章目录 一、聚合报告&#xff08;Aggregate Report&#xff09;参数说明二、准备工作三、测试计划设计 一、聚合报告&#xff08;Aggregate Report&#xff09;参数说明 可以查看事务或者取样器在某个时间范围内执行的汇总结果 使用场景&#xff1a;用于评估测试结果 使用…

2023-06-07 stonedb-包含内连接外连接派生表in子查询和聚合-查询结果错误-分析问题的思路

摘要: 最近在处理stonedb的一个包含内连接包含内连接外连接派生表in子查询和聚合的查询出错的问题, 逻辑非常复杂, 包含的操作符非常多. 本文首先从顶层设计出发, 指出如何分析如此复杂的问题。 查询SQL: SELECTB.company_id,上划日 ud_type,2 sort_no,合计 fiscal_date,DAT…

输入java -version 命令行没反应的简单解决办法【亲测有效】

&#x1f4a7; 记录一下今天遇到的 b u g \color{#FF1493}{记录一下今天遇到的bug} 记录一下今天遇到的bug&#x1f4a7; &#x1f337; 仰望天空&#xff0c;妳我亦是行人.✨ &#x1f984; 个人主页——微风撞见云的博客&#x1f390; &#x1f433; 数据结构与算法…

qemu+buildroot+linux arm64操作系统虚拟化-宿主系统wsl2

文章目录 1.qemu2.buildroot配置编译 3.linux kernel下载交叉编译工具链 linux kernel 5.16配置内核config_kernel.shbuild_kernel.sh 4.启动虚拟机start_qemu.sh参数解释运行 环境&#xff1a; wls2、qemu8.2、buildroot、linuxkernel 1.qemu https://buildroot.org/download…

Python知识点:lambda 表达式

大家好&#xff0c;欢迎来到 Crossin的编程教室 &#xff01; Python 是一门简洁的语言&#xff0c;lambda 表达式则充分体现了 Python 这一特点。 lambda 表达可以被看做是一种匿名函数。它可以让你快速定义一个极度简单的单行函数。譬如这样一个实现三个数相加的函数&#xf…

学会使用perf性能分析工具(含移植到开发板)

文章目录 一、在ubuntu中使用apt包下载Perf二、使用源码安装Perf&#xff0c;并移植到arm-linux环境下三、使用perf四、Perf的功能介绍 系统&#xff1a;Ubuntu18.04系统 内核版本&#xff1a;5.4.0-150-generic&#xff08;通过uname -r查看&#xff09; 一、在ubuntu中使用ap…

Linux驱动系列-PWM驱动

转自&#xff1a;嵌入式系统研发 1.概述 本文主要讲述了Linux的PWM驱动框架、实现方法、驱动添加方法和调试方法。 示例Linux内核版本&#xff1a;6.2.8。 2.原理 PWM是Pulse-Width Modulation的简称&#xff0c;中文译作脉冲宽度调制。作为一种调制技术&#xff0c;PWM的…

SpringBoot实现异步调用的几种方式

一、使用 CompletableFuture 实现异步任务 CompletableFuture 是 Java 8 新增的一个异步编程工具&#xff0c;它可以方便地实现异步任务。使用 CompletableFuture 需要满足以下条件&#xff1a; 异步任务的返回值类型必须是 CompletableFuture 类型&#xff1b; 在异步任务中…