Vue学习笔记

1. 邂逅VueUrlL

1.1 环境准备
  1. 使用WebStorm进行开发

  2. 安装Vue的方式

    image-20210305102912295

1.2 初体验
  1. 入门

    1. 写一段简单代码
    <script src="../js/vue.js"></script>
    
    <div id="app">{{message}}</div>
    
    <script>
        // let(变量) const(常量)
        const app = new Vue({ //Vue传参数,对象类型,大括号里面的是对象的属性
            el: '#app', //用于挂载要管理的元素
            data: { //定于数据
                message: '你好啊,李银河',
            }
        })
    </script>
    
    1. 分析

    浏览器先显示前面的数据,后面得到message的数据后再覆盖,响应式编程,可以实时改变数据

    image-20210305105424683

    data属性也可能是来自网络,从服务器加载的

    1. 声明式编程和响应式编程

    以上这种事声明式编程,类似于面向对象

    元素js的做法是命令式编程,类似于面向过程,一步一步严格按照规则

    1. 创建div元素,设置id属性
    2. 定义一个变量叫message
    3. 将message变量放在前面的div元素中显示
  2. 列表展示

<div id="app">
    <ul>
        <li v-for="item in movies">{{item}}</li>
    </ul>
</div>
...
data: {
            movies: ['星际穿越', '大话西游', '让子弹飞']
        }

image-20210305113804825

  1. 计数器

    <div id="app">
        当前计数:{{count}}
        <br>
    <!--    <button v-on:click="count++">+</button>-->
    <!--    <button v-on:click="count--">-</button>-->
        <button @click="add">+</button>
        <button @click="sub">-</button>
    </div>
    
    <script src="../js/vue.js"></script>
    <script>
        const app = new Vue({
            el: '#app',
            data: {
                count: 0
            },
            methods: {
                add: function (){
                    console.log('add被执行');
                    this.count++
                },
                sub: function (){
                    console.log('sub被执行');
                    this.count--
                }
            }
        })
    </script>
    
1.3 相关知识
  1. mvvm

    image-20210305194106173

    在上节的计数器中演示各部分代码分别是什么成分

    image-20210305195542017

    为了更方便理解,data可以单独写成一个对象

  2. Vue中的options选项

    1. el:
      • 类型:string | HTMLElement
      • 作用:决定之后Vue实例会管理哪一个DOM
    2. data:
      • 类型:Object | Function (组件当中data必须是一个函数)
      • 作用:Vue实例对应的数据对象
    3. methods:
      • 类型:{[key:string]:Function}
      • 作用:定义属于Vue的一些方法,可以在其他地方调用,也可以在指令中使用
  • 方法和函数:直接定义在外面的叫函数,定义在类里面的一般叫方法
  1. Vue的生命周期

    在运行vue函数的过程中,会依照生命周期执行里面的生命周期函数

    image-20210305205254913

2. 基本语法

2.1 插值操作(文本中)
  • mustache语法,就是所谓的{{}}
  • v-once:表示元素和组件只渲染一次,不会随着数据的改变而改变
  • v-html:希望得到的包含链接的参数链接依然可用
  • v-text:类似于mustache,但是是写在标签内部的解析
  • v-pre:直接原封不动地输出内容,不做解析
  • v-cloak:在vue语法加载成功之前不显示未成功解析的{{}}
2.2 v-bind 动态地决定属性值
  1. 示例

    <div id="app">
      <img v-bind:src="imgUri" alt="">
    <!--  语法糖写法-->
      <a :href="aHref">百度一下</a>
    </div>
    
    <script src="../js/vue.js"></script>
    <script>
      const app = new Vue({
          el: '#app',
          data: {
              imgUri: 'https://m.360buyimg.com/babel/jfs/t1/153690/12/20982/113203/603f4e6aE0e5a373b/7704dbfc9071090b.jpg',
              aHref: 'http://www.baidu.com'
          }
      })
    </script>
    
  2. 动态绑定class(对象语法)

<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <style>
    .active{
      color: red;
    }
  </style>
</head>
<body>

<div id="app">

<!--  <h2 :class="active">{{message}}</h2>-->
<!--  一个标签可以同时拥有多个类,如果有一个类必有的可以再写一个,会合并-->
  <h2 :class="{active: isActive, line: isLine}">{{message}}</h2>
  <button @click="btnClick">更换颜色</button>

</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
      el: '#app',
      data: {
          message: '你好啊',
          //类可以替换,写出来多个类的需求与否也可以替换
          // active: 'active'
          isActive: true,
          isLine: false
      },
      methods: {
          btnClick: function (){
              this.isActive = !this.isActive
          }
      }
  })
</script>

</body>

image-20210306143426970

上图的class就只有active,没有line了,如果嫌太长可以直接写成一个方法的返回值

  1. 数组语法(用得少)

​ :class="[active,line]"

​ 如果想表示成字符串就加单引号,如果想表示成变量就不用加

​ 题目不会做。。。

  1. 动态绑定style(对象语法)

    <h2 :style="{fontSize: '50px'}">{{message}}
    <h2 :style="{fontSize: finalSize + 'px', backgroundColor: finalColor}">{{message}}
    
  2. 数组语法(用得少)

    数组里就放一个个单个的对象,对象具体定义在data里面

2.3 计算属性
  1. 特征

    计算属性是computed函数,而且里面的命名一般不加get等动词,因为它本质就是个属性,使用不需要加小括号

    它是有缓存的,不会重复调用,比methods性能更高一点

  2. 使用

    <div id="app">
      <h2>总价格为:{{totalPrice}}</h2>
    </div>
    
    <script src="../js/vue.js"></script>
    <script>
      const app = new Vue({
          el: '#app',
          data: {
              books: [
                  {id: 100, name: 'java', price: 100},
                  {id: 101, name: 'js', price: 101},
                  {id: 102, name: 'python', price: 102},
                  {id: 103, name: 'c++', price: 103},
              ]
          },
          computed: {
              totalPrice: function (){
                  let result = 0
                  for(let i=0;i<this.books.length;i++){
                      result += this.books[i].price
                  }
                  return  result
              }
          }
      })
    </script>
    
  3. set和get方法

    计算属性原本是有get和set方法的,一般set方法不用,赋值的时候才会调用这个方法,所以不写就默认是写在get方法里面

2.4 ES6语法补充
  1. 用let代替var

    var太猖狂了,可以随意跨作用域,闭包才可以解决这个问题,因为函数是一个作用域

    ES5中的var是没有块级作用域的(if/for)

    ES6中的let是有块级作用域的(if/for)

    理解下面这段代码

    <script>
    
      const btns = document.getElementsByTagName('button');
      for (let i=0;i<btns.length; i++){ //如果写成var就是错的
          btns[i].addEventListener("click", function (){
            console.log('第' + i + '个按钮被点击');
          })
      }
    </script>
    

    在点击按钮之前,for循环就会先执行一遍,直接生成按钮数量个监听按钮器

  2. const的使用

    • 在开发中优先使用const,只有需要改变某一个标识符的时候才使用let
    • const定义常量,不可改变
    • 在使用const定义标识符,必须进行赋值
    • 常量的含义是指向的对象不能修改,但是可以改变对象内部的属性(保存的是对象的内存地址)
  3. 对象字面量增强写法

    1. 属性的简写:如果之前定义的key=value,传到对象的时候只要写一个key就行了
    2. 方法的简写:省略了:function
2.5 v-on
  1. 参数传递问题
    1. 如果没有参数,调用方法的时候可以不加小括号
    2. 有参数如果省略参数,只写小括号,就会传入undefined进入
    3. 如果有参数的情况下连小括号都不写,就会传入点击事件event进入
    4. 如果本来就要传入event参数,调用的实参写成$event
  2. 修饰符的使用
    1. @click.stop:调用 event.stopPropagation() 停止冒泡
    2. .prevent:调用event.preventDefault() 阻止默认行为
    3. .{keyCode | keyAlias}:只当事件是从特定键触发时才触发回调
    4. .once:只触发一次回调
2.6 v-for
  1. 遍历数组

    <li v-for="(item,index) in items">{{index}}-{{item}}</li>
    
  2. 遍历对象

    <div id="app">
      <ul>
    <!--    如果不加key和index,默认只有value-->
        <li v-for="(value,key,index) in books">{{value}}-{{key}}-{{index}}</li>
      </ul>
    </div>
    
    <script src="../js/vue.js"></script>
    <script>
      const app = new Vue({
          el: '#app',
          data: {
              books: {
                  aaa: '111',
                  bbb: '222'
              }
          }
      })
    </script>
    
  3. 提升算法效率

    为了在数组中间插入元素的效率更高,可以通过绑定key的方式,与原理有关

    <li v-for="item in letters" :key="item">{{item}}</li>
    

    虚拟dom的原理,原来在中间插入元素的时候,会直接占用那个元素的位置,后面的元素再依次覆盖,现在这样写就不会了

2.7 响应式方法有哪些

push,pop,shift,unshift,splice,sort,reverse

通过索引值修改数组中的元素不是响应式的,如果想要改变,可以通过splice方法和set方法改变,推荐splice

2.8 综合案例:书籍购物车案例

主要问题是不知道变量的范围,不知道什么时候加this

2.9 JavaScript高价函数的使用
  1. for循环里面的in和of(对于数组里面是一个个对象)

    • in就是简写版,返回的是下标

    • of是增强版,返回的是数组遍历过程中一个个含下标的对象

      totalPrice() {
          let result=0
          for (let item of this.books){
              result += item.price + item.count
          }
      }
      
  2. 数组.filter/.map/.reduce

    const nums = [10,20,111,222,444,40,50]
    
    //filter获取数组里面小于100的元素存入新数组
    let total = nums.filter(function (n){
        return n < 100  //返回值为true就可以存入数组
    //map把所有的数组元素*2再存入
    }).map(function (n){
        return n * 2
    //返回数组中所有元素的和
    }).reduce(function (prevValue,n){
        return prevValue + n
    },0)
    console.log(total);
    
2.10 v-model
  1. 双向绑定的使用和原理

    <div id="app">
      <input type="text" v-model="message">
      {{message}}
    </div>
    
    <script src="../js/vue.js"></script>
    <script>
      const app = new Vue({
          el: '#app',
          data: {
              message: '你好啊'
          }
      })
    </script>
    

    输入任何数据,页面和实际内容都会一起改变

    image-20210308143141140

    <input type="text" v-model="message">
    等同于
    <input type="text" v-bind:value="message" v-on:input="message=">
    
  2. 在选择框的使用

    1. radio
    2. checkbox
    3. select
    4. 值绑定:让选择框中的数据不是写死的,从数组中动态读取
  3. 修饰符

    1. .lazy:失去焦点和回车的时候才会进行更新
    2. .number:不让输入的数字自动转变为string类型,转为数字类型
    3. .trim:忽略输入的空格

3. 组件化

3.1 组件化的基本过程
div id="app">

  //3.使用组件
  <my-cpn></my-cpn>
  <my-cpn></my-cpn>
</div>

<script src="../js/vue.js"></script>
<script>

  //1.创建组件构造器对象
  const cpnC = Vue.extend({
    template:`
    <div>
      <h2>我是标题</h2>
      <p>我是内容111</p>
      <p>我是内容222</p>
    </div>`
  })

  //2.注册组件(这样注册的是全局组件,意味着可以在多个Vue的示例下面使用)
  Vue.component('my-cpn',cpnC)

  //可以认为这是一个顶级组件
  const app = new Vue({
      el: '#app',
      data: {
          message: '你好啊'
      }
  })
</script>
3.2 局部组件

以上直接注册的组件时全局组件,在Vue实例里面注册的才是局部组件

const app = new Vue({
    el: '#app',
    data: {
        message: '你好啊'
    },
    components: {
      cpn: cpnC
    }
})
3.3 父组件和子组件
const cpnC2 = Vue.extend({
  template:`
    <div>
      <h2>我是标题</h2>
      <p>我是内容111</p>
      <p>我是内容222</p>
      <cpn1></cpn1>
    </div>
  `,
  //在组件二里面注册组件一并使用,代表组件二是组件一的父组件,之后直接使用组件二就行,不格外注册组件一就不能直接用组件一
  components: {
    cpn1: cpnC1
  }
})
3.4 语法糖
  1. 全局组件

    Vue.component('cpn',{
      template:`
      <div>
        <h2>我是标题</h2>
        <p>我是内容</p>
      </div>`
    })
    
  2. 局部组件

    //root
    const app = new Vue({
        el: '#app',
        data: {
            message: '你好啊'
        },
        components: {
          'cpn2': {
            template:`
              <div>
                <h2>我是标题</h2>
                <p>我是内容</p>
              </div>`
          }
        }
    })
    
3.5 可以把template里面的代码抽取出来

在外面格外定义一个templated标签,属性加上id,通过 template:’#id’ 引入就行

3.6 组件data

组件不能直接使用Vue中的data,只能够使用组件中定义的data,其必须是一个函数

为什么呢?

因为只有函数return返回的每个地址都不相同,这样对于共用同一个变量进行计数的情况,就不会产生连锁反应

3.7 父子组件的通信
  1. 父传子用props

    <div id="app">
      <cpn :cmovies="movies" :cmessage="message"></cpn>
    </div>
    
    <template id="cpn">
      <div>
    <!--    <p>{{cmovies}}</p>-->
        <ul>
          <li v-for="item in cmovies">{{item}}</li>
        </ul>
        <h2>{{cmessage}}</h2>
      </div>
    </template>
    
    <script src="../js/vue.js"></script>
    <script>
    
      const cpn = {
        template : '#cpn',
        //使用数组方式
        // props: ['cmovies','cmessage'],
        //对象方式
        props: {
          // 1.类型限制
          // cmovies: Array,
          // cmessage: String,
          // 2.提供一些默认值,以及必传值
          cmessage: {
            type: String,
            default: 'aaaaaa',
            request: true
          },
          cmovies: {
            type: Array,
            default: []
          }
        },
        data() {
          return {}
        },
        methods: {
    
        }
      }
    
      const app = new Vue({
        el: '#app',
        data: {
          message: '你好啊',
          movies: ['海王','海贼王','海尔兄弟']
        },
        components: {
          cpn  //增强写法
        }
      })
    </script>
    

    html里面不能用驼峰标识,只能用 - 加小写代替

    template标签里的东西最好用div标签包裹起来

  2. 子传父用自定义事件

    <!--父组件模块-->
    <div id="app">
      <cpn @item-click="cpnClick"></cpn>
    </div>
    
    <!--子组件模块-->
    <template id="cpn">
      <div>
        <button v-for="item in categories" @click="btnClick(item)">
          {{item.name}}
        </button>
      </div>
    </template>
    
    <script src="../js/vue.js"></script>
    <script>
    
    <!--  子组件-->
      const cpn = {
        template : '#cpn',
        data() {
          return {
            categories: [
              {id:'aaa',name:'热门推荐'},
              {id:'bbb',name:'手机数码'},
              {id:'ccc',name:'家电办公'},
              {id:'ddd',name:'电脑办公'},
            ]
          }
        },
        methods: {
          btnClick(item){
            // 发射事件:自定义事件
            this.$emit("item-click",item)
          }
        }
      }
    
      // 父组件
      const app = new Vue({
        el: '#app',
        data: {
          message: '你好啊'
        },
        components: {
          cpn
        },
        methods: {
          cpnClick(item){
            console.log('cpnClick',item)
          }
        }
    
      })
    </script>
    
    • 在子组件中,通过$emit()来触发事件
    • 在父组件中,通过v-on来监听子组件事件
  3. 结合双向绑定

    跳过,后看

3.8 父子组件的访问方式
  1. 父访问子

    1. $children

      比如button在父组件,在定义的点击事件函数,可以通过this.$children.*** 直接使用子组件的data、methods等

    2. $refs

      主要是用这个,因为这个可以通过里的ref直接指定是哪个组件

      console.log(this.$refs.***.***)
      
  2. 子访问父(用得很少)

    1. $parent

      button在子组件,用法与上面相反

    2. $root 访问祖宗属性

4. 插槽

4.1 为什么要用插槽

很多时候都要求复用组件,但是服用的组件需求可能会不会,比如不同页面的导航栏

image-20210309164336961

4.2 如何使用

抽取共性,保留不同。相同的写好,不同的预留成插槽,在使用的时候可以一起替换

如果插槽里面有的就是默认值,不写替换元素的话直接显示默认值

image-20210309165116413

image-20210309165204102

4.3 具名插槽

就是给插槽取名字,方便具体替换哪个插槽

image-20210309165634119

4.4 作用域插槽
  1. 编译作用域的概念

    当前模块的代码只能够访问对应的组件,不能跨作用域

  2. 使用

    父组件对子组件不满意,想以另外一种方式展示

    父组件替换插槽的标签,但是内容由子组件来提供

    代码用的时候再查

5. 模块化

5.1 为什么要有模块化

有很多js文件的时候,命名会冲突,所以需要封闭编写的函数,留出一个出口方便后面的函数调用

5.2 ES6模块化的导出导入
  1. 导出

    1. 先定义变量,函数,类,再导出
    2. 直接导出
  2. 导入

    1. 单个导入
    2. 全部取别名导入
  3. default导出

    不希望给这个功能取名,让导入者可以自己来命名,一个文件只能定义一次

  4. 引入js代码时加上 type=“module”

5.3 Webpack
  1. 概念:模块+打包

  2. webpack和node和npm关系

    image-20210309192336157

  3. 基本使用

    编写js文件,写好之间的依赖。运行打包的命令生成bundle.js在dist文件夹就可以了

  4. ES6转ES5需要使用babel

  5. 创建Vue时template和el的关系

    vue内部如果同时有el和template:***(这个是主属性,不是子模块里面的这个),template里的代码会覆盖index.html页面的

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

    在模块化开发之后,这样可以不用手动频繁修改

    这里的template使用template:’<App’/>

  6. Vue的终极使用方案

    前面这样做,代码可能会很冗余,又是一步步抽取出来

    原来除了index页面就这行,很简洁,但是负责控制它的main.js页面就很复杂了,需要拆分抽取

    1. 把root组件的template,data,methods等全部抽取到const App子组件中,再引入
    2. 可以把const App格外创建一个.js文件,作为默认值引入
    3. 2方法还是不够简洁,因为js文件里面这个多东西还是不太好看,把里面的各个部分都移入到新创建的.vue文件对应的部分中
    4. 可以在这个.vue文件中再在script部分中引入其它.vue子组件

    npm stall命令是根据package.json里面的版本安装各种工具

    npm run build 和 npm run dev 的区别 npm run serve

    这里我还没有下载webpack

5.4 VueCLI3(我用的是4)
  1. 作用

    可以快速搭建Vue开发环境以及对应的webpack配置

  2. 使用方式

    1. 先下载webpack和vue-cli

    2. 运行命令创建项目

      vue create testvuecli3

  3. 创建项目分析

    node_modeles:npm下载的东西放这里

    public:代替以前的static,放资源和index

    src:主代码

    .browserslistrc:浏览器相关的配置

    .babel.config.js:对babel的配置

    package.json:配置文件

    image-20210310143246750

  4. 运行项目

    1. cd进入当年目录
    2. npm run server
  5. main.js代码分析

    new Vue({
      render: h => h(App),
    }).$mount('#app')
    
    相当于
    new Vue({
      el:'#app',
      render: funtion(h){
        return h(App)
      }
    })
    
    相当于vue 1.0写法
    new Vue({
      el: '#app',
      template:'</App>'
      componets: {App}
    })
    
    render就是把template模板里面的页面渲染出来
    这是vue3的写法
    
  6. 配置去哪里了

    1. 启动配置服务器:vue ui
    2. 可以在node_modules里面找到
    3. 自定义配置文件vue.config.js
5.5 箭头函数
  1. 普通函数写法

    const sum1 = function (x,y){
      return x+y
    }
    
    //这种语法时对象的写法,定义的无名函数就是increment方法键的值,可以当作两步
    increment: function (){  
    }
    也等于
    increment(){
    }
    
    
  2. 常数作为函数名的写法

    const sum2 = function (x,y){
        return x+y
    }
    若不写参数,默认返回整个函数体
    function (x,y){
        return x+y
    }
    
  3. 箭头函数写法

    //经典格式: 函数名 = ( 参数 ) => { 方法体 } 
    const sum3 = (x,y)=>{ 
        return x+y; 
    }
     
    //当方法体只有一行时,花括号可以省略: 
    const sum4 = (x,y) => x+y;  
     
    //当只有一个参数时,括号可以省略: 
    const sum5 = x => { 方法体 }
    
  4. 箭头函数中的this引用的就是最近作用域中的this

    const obj = {
      aaa(){
        setTimeout(function (){
          console.log(this); //window
        })
    
        setTimeout(() => {
          console.log(this); //obj对象
        })
      }
    }
    obj.aaa()
    

6. 路由

6.1 发展的几个阶段
  1. 后端渲染和路由

    早期的网站开发整个html页面是由服务器来渲染的,然后返回给客户端进行展示

    image-20210310192407777

  2. 前后端分离阶段

    image-20210310192514465

    随着Ajax的出现,有了前后端分离的开发模式,后端只提供API来返回数据,前端通过Ajax获取数据,并且可以通过JavaScript将数据渲染到页面上,而且当移动端(ios/Android)出现后,后端不需要进行任何处理,依然使用之前的一套API即可

  3. 前端路由阶段(SPA页面)

    image-20210310193927547

    image-20210310193942027

6.2 url的hash和history

image-20210310204931643

还有history.replaceState({},’’,’***’) 这个是直接替换而不保存栈

history.back()等价于history.go(-1)

history.forward()等价于history.go(1)

这三个接口等用于浏览器界面的/前进后退

6.3 路由的安装
  1. npm安装路由

  2. 编写路由代码index.js

    1. 导入路由对象,并且调用Vue.use(VueRouter)
    2. 创建路由实例,并且传入路由映射配置
    3. 在Vue实例中挂载创建的路由实例
    // 配置路由相关的信息
    import VueRouter from 'vue-router'
    import Vue from 'vue'
    
    //导入后面创建的.vue组件
    
    //1.通过Vue.use(插件),安装插件
    Vue.use(VueRouter)
    
    // 2.创建VueRouter对象,先抽取出来写
    const routes = [
      {
        path:'/',
        component: 
      },
      {
        path: '/',
        component: 
      }
    ]
    const router = new VueRouter({
      //配置路由和组件之间的映射关系
      routes
    })
    
    //3.将router对象传入到Vue实例,先导出
    export default router
    
6.4 使用vue-router的步骤
  1. 创建路由组件 各个component页面 .vue

  2. 配置路由映射:组件和路径映射关系

    import Home from "../components/Home"
    import About from "../components/About"
    ***
    const routes = [
      {
        path:'/home',
        component: Home
      },
      {
        path: '/about',
        component: About
      }
    ]
    const router = new VueRouter({
      //配置路由和组件之间的映射关系
      routes
    })
    ***
    
  3. 使用路由:通过和

    App.vue
    
    <template>
      <div id="app">
      	//默认被渲染成一个a标签
        <router-link to="/home">首页</router-link>
        <router-link to="/about">关于</router-link>
        //占位的一个东西
        <router-view></router-view>
      </div>
    </template>
    
  4. main.js代码

    import Vue from 'vue'
    import App from './App.vue'
    import router from './router'
    
    Vue.config.productionTip = false
    
    new Vue({
      //那两个.vue就是App.vue的子模块,因为下面router的传递
      router,
      render: h => h(App)
    }).$mount('#app')
    
6.5 路由的一些修改
  1. 让进入项目时默认显示首页

    路由文件加一个
    {
      path:'/',
      redirect:'/home'
    },
    
  2. 改为history模式,浏览器地址也不会再多显示一个#

    image-20210311133425644

    image-20210311133732331

  3. router-link的其它属性补充

    image-20210311135151257

  4. 通过代码跳转路由

    image-20210311140521863

6.6 动态路由的使用

根据不同的userId为例,期待可以根据拿到的数据动态路由到不同的ip地址,并且显示userId

  1. 更改主页面router-link标签地址

    image-20210311161512956

  2. 添加路由映射配置

    image-20210311161728362

  3. 修改显示页面

    <template>
      <div>
        <h2>我是用户</h2>
        <p>嘿嘿嘿嘿嘿</p>
        {{userId}}
        //{{$route.params.userId}}也行,注意在这里使用不用加this
      </div>
    </template>
    
    <script>
      export default {
        name: "User",
        computed: {
          userId() {
            return this.$route.params.userId
          }
        }
      }
    </script>
    
    <style scoped>
    
    </style>
    
  4. 页面显示

    image-20210311162345179

  • r o u t e r 和 router和 routerroute的区别: r o u t e r 就 是 n e w 的 这 个 V u e R o u t e r , router就是new的这个VueRouter, routernewVueRouter,route是拿到的处于活跃状态的对象
6.7 懒加载
  1. 为什么要用懒加载

    当打包构建应用时,Javascript包会变得非常大,影响页面加载,如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了

  2. 路由懒加载的效果

    image-20210311170308825

  3. 懒加载代码

    import Home form '../components/Home'
    改为
    const Home = () => import('../components/Home')
    
6.8 vue-router路由嵌套

在home主页又嵌入两个路由

  1. 分别创建两个小组件

  2. 在Home.vue写路由跳转信息

    <template>
      <div>
        <h2>我是首页</h2>
        <p>哈哈哈哈哈</p>
    
        <router-link to="/home/news">新闻</router-link>
        <router-link to="/home/message">消息</router-link>
    
        <router-view></router-view>
      </div>
    </template>
    
  3. 在路由js文件中在home映射下面再嵌套子映射

    {
      path:'/home',
      component: Home,
      children: [
        {
          //默认打开第一个
          path:'',
          redirect:'news'
        },
        {
          path:'news',
          component: HomeNews
        },
        {
          path: 'message',
          component: HomeMessage
        }
      ]
    },
    

    image-20210311195646713

6.9 vue-router参数传递

把查询参数通过浏览器的?和&传过去

  1. 新建一个组件Profile.vue

  2. 再Vue.vue主页面写匹配的访问地址

    <router-link :to="{path: '/profile', query: {name: 'why',age: 18, height: 1.88}}">档案</router-link>
    
  3. 路由.js文件

    {
      path: '/profile',
      component: Profile
    }
    
  4. 在跳转页面接收query的参数

    <template>
      <div>
        <h2>我是Profile组件</h2>
    
        {{$route.query.name}}
        {{$route.query.age}}
        {{$route.query.height}}
      </div>
    </template>
    

    URL:协议://主机:端口/路径?查询

    scheme://host:port/path?query#fragmet

  5. 改为按钮

    <!--    <router-link :to="'/user/'+userId">用户</router-link>-->
    <!--    <router-link :to="{path: '/profile', query: {name: 'why',age: 18, height: 1.88}}">档案</router-link>-->
        <button @click="userClick">用户</button>
        <button @click="profileClick">档案</button>
    
    methods: {
      userClick(){
        this.$router.push('/user/'+this.userId)
      },
      profileClick(){
        this.$router.push({
          path: '/profile',
          query: {
            name: 'why',
            age: 18,
            height: 1.88
          }
        })
      }
    }
    
  6. router和route

    在User.vue中通过methods点击事件拿到router和route后分别打印
    image-20210312111329687

    第一个是new的VueRouter

    第二个是最近活跃的

    image-20210312111527930

    所有的组件都继承自vue的原型,如果给vue的原型加个方法或者变量,每个组件都会收影响

    Vue.prototype. --vue原型

6.10 导航守卫
  1. 更改路由访问不同页面的标题

    1. 在组件中一个个改

      <script>
      export default {
        name: "Home",
        created() {
          document.title = '首页'
        }
      }
      </script>
      

      太麻烦了,由于跳转都是经过路由,可以使用方法2

    2. 使用导航守卫

      先在路由index.js中给每个跳转映射加一个title名字

      {
        path: '/about',
        component: About,
        meta:{
          title: '关于'
        },
      },
      

      前置守卫

      //前置守卫(hook)
      //从from跳转到to
      router.beforeEach((to,from,next) => {
        document.title = to.matched[0].meta.title,
        //next函数必须要有
        next()
      })
      
  2. 导航守卫的补充

    1. 全局守卫
      1. 前置守卫即上面这个,路由跳转之前进行的回调
      2. router.afterEach,后置钩子,不需要主动调用next()函数,路由跳转之后
    2. 局部守卫
      1. 路由独享的守卫
      2. 组件内的守卫
6.11 keep-alive

离开某个组件的时候不让组件频繁被创建和销毁,可以保持原状

<keep-alive exclude="Profile,User"> //忽略多个用逗号隔开,不能加空格
	<router-view></router-view>
</keep-alive>

只有写了keep-alive才能使用activated和deactivated这两个函数,是否保持活跃

7. Promise

7.1 出现原因

当网络请求非常复杂的时候,会出现回调地狱

7.2 使用Promise
<script>
  new Promise((resolve, reject) => {
    //第一次网络请求的代码
    setTimeout(() => {
      resolve()
    },1000)
  }).then(() => {
    //第一次拿到结果的处理代码
    console.log('Hello World');
    console.log('Hello World');
    console.log('Hello World');
    console.log('Hello World');

    return new Promise((resolve, reject) => {
    //第二次网络请求的代码
      setTimeout(() => {
        resolve()
      },1000)
    })
  }).then(() => {
    //第二次处理的代码
    console.log('Hello Vuejs');
    console.log('Hello Vuejs');
    console.log('Hello Vuejs');
    console.log('Hello Vuejs');

    return new Promise((resolve, reject) => {
      //第三次网络请求的代码
      setTimeout(() => {
        resolve()
      },1000)
    })
  }).then(() => {
    // 第三次处理的代码
    console.log('Hello Python');
    console.log('Hello Python');
    console.log('Hello Python');
    console.log('Hello Python');
  })
</script>
7.3 成功和失败
new Promise((resolve, reject) => {
  //第一次网络请求的代码
  setTimeout(() => {
    //成功的时候调用resolve
    // resolve('Hello world')

    //失败的时候调用reject
    reject('error message')
  },1000)
}).then((data) => {
  //第一次拿到结果的处理代码
  console.log('data');
  console.log('data');
  console.log('data');
  console.log('data');
}).catch((err) => {
  console.log(err);
})
7.4 三种状态
  1. pending:等待状态
  2. fulfill:满足状态,回调resolve时就处于该状态,并且会回调.then()
  3. reject:拒绝状态,回调reject时就处于该状态,并且会回调.catch()

另一种写法

new Promise((resolve, reject) => {
  //第一次网络请求的代码
  setTimeout(() => {
    //成功的时候调用resolve
    // resolve('Hello world')

    //失败的时候调用reject
    reject('error message')
  },1000)
}).then(成功的方法1,失败的方法2)
7.5 链式调用及简写
  1. 原代码

    return new Promise(resolve => {
      //给res字符串拼接上222
      resolve(res + '222')
    })
    
  2. 第一次简写:new Promise(resolve => resolve(结果))

    return Promise.resolve(res + '222')
    or
    return Promise.reject('error message') |
    throw 'error message'
    
  3. 第二次简写:省略掉Promise.resolve

    return res+'222'
    
7.6 all方法的使用

如果要两个请求都请求到了再开始执行

<script>
  //模拟真实的请求
  Promise.all([
      new Promise((resolve, reject) => {
        //逻辑代码1
      }),
    new Promise((resolve, reject) => {
      //逻辑代码2
    })
      //results里面放上面这两个对象
  ]).then(results => {
    //对results的处理
  })
</script>

8. Vuex

8.1 Vuex概念

把公共的变量放在一个单例对象进行集中管理,而且可以做到响应式。父子组件传信息不用放在Vuex中

image-20210312212455713

8.2 单界面到多界面状态管理切换
  1. 单页面的状态管理

    image-20210313102528464

  2. 如果组件之前关系复杂

    1. 安装vuex命令

      npm install vuex --save

    2. 在store文件夹创建index.js

      import Vue from 'vue'
      import Vuex from 'vuex'
      
      Vue.use(Vuex)
      
      export default new Vuex.Store({
        //共享的状态,对应官方给出图片的对应部分
        state: {
        	counter: 1000
        },
        mutations: {
        },
        actions: {
        },
        modules: {
        }
      })
      
    3. 在main.js里导入vuex

    4. 现在可以共享了,在components文件夹创建子组件HelloVue.vue

      <template>
        <div>
          <h2>{{$store.state.counter}}</h2>
          //在这里直接通过vue components直接控制counter不好,因为没有经过mutations记录不到
        </div>
      </template>
      
      <script>
      export default {
        name: "HelloVuex"
      }
      </script>
      
      <style scoped>
      
      </style>
      
    5. 根组件

      <template>
        <div id="app">
          <h2>{{$store.state.counter}}</h2>
      <!--    在components里面注册过了这个子组件,所以有这个标签-->
          <hello-vuex/>
        </div>
      </template>
      
      <script>
      import HelloVuex from './components/HelloVuex'
      
      export default {
        name: 'App',
        components: {
          HelloVuex
        },
        data() {
          return {
            message: '我是App组件'
          }
        }
      }
      </script>
      
8.3 Vuex的状态管理

​ 以通过vuex对counter加减为例

  1. 官方图片

    image-20210313112124781

  2. 谷歌商店下载devtools插件

  3. mutations里面定义函数

    mutations: {
      increment(state){
        state.counter++
      },
      decrement(state){
        state.counter--
      }
    },
    
  4. 通过按钮使用这两个函数

    methods: {
      addition(){
        this.$store.commit('increment')
      },
      subtraction(){
        this.$store.commit('decrement')
      }
    }
    
  5. 可以通过devtools监听到每个事件的状态

    image-20210313114346050

8.4 Vuex核心概念
  1. state单一状态树

    只要创建一个state就行了

  2. getters的使用详解

    1. 作用

      可以代替计算属性,而且所有人都能使用,不要重复造轮子

    2. 定义一个变量和一个对象数组

      state: {
        counter: 1000,
        students: [
          {id: 110, name: 'why', age: 18},
          {id: 111, name: 'kobe', age: 24},
          {id: 112, name: 'james', age: 30},
          {id: 113, name: 'curry', age: 10},
        ]
      },
      
    3. 定义处理数组的getters函数

      image-20210313154311540

    4. 组件中的使用

      image-20210313154616714

    5. 展示结果

      image-20210313154742403

  3. Mutation

    1. 状态更新

      只要改store,就要提交Mutation

    2. 传递参数

      image-20210313160243924

    3. 两种提交风格

      1. 上面的通过commit提交时一种普通的风格

      2. 另一种风格,它是一个包含type属性的对象

        image-20210313161305030

      image-20210313161443453

    4. 一种约定俗成的规矩

      为了两边名字能够写对,定义方法名称的时候写成常量形式,格外定义一个js模块保存方法名称为常量,再在使用的时候import

  4. 数据的响应式原理

    1. 为什么能够做到响应式

      vuex给state里的每个对象里的每个属性都加入了一个监听,发生变化后在网页上会实时响应

      image-20210313165537000

    2. 什么情况下没有响应式

      直接使用普通方法添加和删除属性的时候不能实现响应式,要换一个函数

    3. 代码实例

      给对象添加删除属性

      image-20210313170006508

  5. Action(多参照官方提供的图)

    1. 作用

      在Mutation中不能够出现异步的情况,因为这样的话devtools时监听不了的,监听显示的是错误的数据,Action类似于Mutation,但是是用来代替Mutation进行异步操作的

    2. 异步无参数情况下的使用

      image-20210313175653093

      image-20210313175812261

    3. 异步有一个参数的情况下使用

      定义:aUpdateInfo(context,形参)

      使用:this.$store.dispatch(‘aUpdateInfo’,实参)

    4. 异步传函数的情况下

      用Promise更优雅

  6. modules(套娃)

    1. 作用

      image-20210313194648159

    2. 与state结合

      用modeleA里面的state

      {{$store.state.a.name}}

    3. 与mutations结合

      与之前一样,不用多加

    4. 与getters结合

      与之前差不多,注意使用本getters的其它函数和根state的用法,使用对象的解构把context解构为state,getters,rootState

      (原来之前只写state是因为只要用到state,实际上是写context的),对象的解构按照名字对应

    5. 与actions结合

      这里commit是调用本部分mutations的函数

  7. store文件夹的目录结构

    建议把Vuex的核心属性除state以外全部抽取出来,成为一个个js文件

    image-20210313200802838

9. axios

9.1 asiox框架的基本使用
  1. npm install axios --save

  2. import axios

  3. 访问老师的接口地址

    axios({
      url: 'http://123.207.32.32:8000/home/multidata',
      /methods: 默认是get请求
      //params:{地址后面?带的请求参数也可以写在这里
      //}
    }).then(res => { //axios内部实现了Promise
      console.log(res);
    })
    
  4. 结果

    image-20210313204342052

9.2 发送并发请求
  1. 使用axios,可以放入多个请求的数据

    axios.all([
      axios({
      url: 'http://123.207.32.32:8000/home/multidata',
    }),axios({
      url: 'http://123.207.32.32:8000/home/data',
      params: {
        type: 'sell',
        page: 5
      }
    })
    ]).then(results => {
      console.log(results);
    })
    

    image-20210313211051672

  2. axios.all([])返回的结果是一个数组,使用axios.spread可将数组[res1,res2]展开为res1,res2

    ]).then(axios.spread((res1,res2) => {
      console.log(res1);
      console.log(res2);
    }))
    

    image-20210313211455598

9.3 配置相关的信息
  1. 全局配置

    axios.defaults可以设置公共的默认配置,如:

    axios.default.baseURL = ‘123.207.32.32:8000’

  2. 常见的配置选项

    image-20210313212245886

9.4 axios的实例和模块封装
  1. 问题

    以上是直接使用全局的axios和对应的配置进行网络请求,但是如果后端有多个ip地址的时候就不适用了

  2. 创建对应的axios的实例

    const instance1 = axios.create({
      baseURL: 'http://222.111.32.32:8000',
      timeout: 5000
    })
    
    instance1({
      url: 'home/multitdata'
    }).then(res => {
      console.log(res);
    })
    
    instance1({
      url: '/home/data',
      params: {
        type: 'pop',
        page: 1
      }
    }).then(res => {
      console.log(res);
    })
    

    如果不止一个请求

    const instance2 = axios.create({
      baseURL: 'http://222.111.32.32:8000',
      timeout: 10000
    })
    
  3. 为什么要进行模块封装

    image-20210314200125654

  4. 封装一个axios实例,使用的时候直接调用它就行了

    request.js
    
    import axios from "axios";
    
    //写function不写default是因为如果ip地址不同的话可以添加更多的封装函数
    export function request(config) {
      //创建axios的实例
      const instance = axios.create({
        baseURL: 'http://123.207.32.32:8000',
        timeout: 5000
      })
    
      //发送真正的网络请求
      return instance(config)
    }
    

    调用的函数

    //因为导入时没加default
    import {request} from "./network/request"
    
    request({
      url: '/home/multidata'
    }).then(res => {
      console.log(res);
    }).catch(err => {
      console.log(err);
    })
    
9.5 拦截器
  1. 用于我们在发送每次请求或者得到响应后,进行对应的处理,写在发送真正的网路请求return instance(config)之前

  2. 请求拦截

    instance.interceptors.request.use(config => {
      console.log(config);
      //不返回放行就拦截住了
      return config
      //很少用到error
    }, err => {
      console.log(err);
    })
    
  3. 响应拦截

    instance.interceptors.response.use(res => {
      console.log(res);
      return res.data //可以只返回data
    }, err => {
      console.log(err);
    })
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值