admin 管理员组

文章数量: 887019

1、淘宝镜像
 1: 解释一下 npm 为什么要配置淘宝镜像
      原因:因为node.js 默认使用的是国外的网站 。国内访问有一个跨国内局域网的操作。所以就会有时候很慢。这就跟为什么网站的静态资源有些会使用CDN 加速一样的
     淘宝镜像是什么?就是npm 很多的插件淘宝已经下载好了放在公共的网站上 我们需要的时候去淘宝网上下载 和 国外的是一样  这样使用是提升了我们的下载速度。所以淘宝镜像其实是一个国外插件的 国内版本
 2:安装命令
     npm config set registry https://registry.npm.taobao
 3:查看是否安装成功 
    npm info express
 4:使用命令 
    npm install -g cnpm --registry=https://registry.npm.taobao
     cnpm install <插件名>
3、安装vue的调试工具
1、插件已经下载好,直接拖着往谷歌浏览器的扩展程序里面,记得是开发者模式(教学案例)

1、理解什么是Vue

0:官网https://cn.vuejs/guide/introduction.html
  https://cdn.jsdelivr/npm/vue@2  压缩版
  https://cdn.jsdelivr/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" @click="run">阻止跳转</a><br>
         run(e){
                    e.preventDefault();
                    console.log("阻止成功");
                    

                },

     2、
       <a href="https://www.baidu" @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:
3、yyr
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是没有过滤器的

7、yyr

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: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/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')
yyr
651908398
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、简答类型复制的是值,复杂类型的是引用地址。

13、yyr
 
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/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));
  thisame=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/

2、在官网里面搜索animate.css,点击第一个
   https://www.npmjs/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

本文标签: 这一 Vue 个就够