Vue全家桶入门精细讲解

Vue入门精细讲解

感谢coderwhy老师的精心讲解,本笔记全部内容源于coderwhy老师的课堂笔记;

一. Hello Vuejs

1.1. 认识Vuejs

  • 为什么学习Vuejs

    可能你的公司正要将原有的项目使用Vue进行重构。

    也可能是你的公司新项目决定使用Vue的技术栈。

    当然,如果你现在正在换工作,你会发现招聘前端的需求中,10个有8个都对Vue有或多或少的要求。

    当然,作为学习者我们知道Vuejs目前非常火,可以说是前端必备的一个技能。

  • Vue的读音

    Vue (读音 /vjuː/,类似于 view),不要读错。

  • Vue的渐进式

    渐进式意味着你可以将Vue作为你应用的一部分嵌入其中,带来更丰富的交互体验。

    或者如果你希望将更多的业务逻辑使用Vue实现,那么Vue的核心库以及其生态系统。

    比如Core+Vue-router+Vuex,也可以满足你各种各样的需求

  • Vue的特点

    解耦视图和数据

    可复用的组件

    前端路由技术

    状态管理

    虚拟DOM

    学习Vuejs的前提?

    从零学习Vue开发,并不需要你具备其他类似于Angular、React,甚至是jQuery的经验。

    但是你需要具备一定的HTML、CSS、JavaScript基础。

1.2. 安装Vue

  • CDN引入

  • 下载引入

    开发环境https://vuejs.org/js/vue.js

    生产环境https://vuejs.org/js/vue.min.js

  • npm安装

    npm i vue

1.3. Vue的初体验

  • Hello Vuejs

  •    <div id="app">
            {{message}}
        div>
        <script src="./js/vue.js">script>
        <script>
            const app=new Vue({
                el:'#app',//挂载
                data:{
                    message:'你好啊,李银河'
                }
            })
        script>
    
    • mustache -> 体验vue响应式
  • Vue列表展示

    • v-for

    • <li v-for="item in movies">{{item}}li>
      
       data: {
                  movies: ['星际穿越', '大话西游', '少年派', '盗梦空间']
              }
      
    • 后面给数组追加元素的时候, 新的元素也可以在界面中渲染出来

  • Vue计数器小案例

    • 事件监听: click -> methods

    • <button @click='sub'>-button>
      <h2>{{counter}}h2>
      <button @click='add'>+button>
      
      data:{
         counter:0
           },
       methods:{
       sub(){
        this.counter--
        },
        add(){
        this.counter++
          }
      }
      

1.4. Vue中的MVVM

维基百科介绍:https://zh.wikipedia.org/wiki/MVVM

View层:

视图层

在我们前端开发中,通常就是DOM层。

主要的作用是给用户展示各种信息。

Model层:

数据层

数据可能是我们固定的死数据,更多的是来自我们服务器,从网络上请求下来的数据。

在我们计数器的案例中,就是后面抽取出来的obj,当然,里面的数据可能没有这么简单。

VueModel层:

视图模型层

视图模型层是View和Model沟通的桥梁。

一方面它实现了Data Binding,也就是数据绑定,将Model的改变实时的反应到View中

另一方面它实现了DOM Listener,也就是DOM监听,当DOM发生一些事件(点击、滚动、touch等)时,可以监听到,并在需要的情况下改变对应的Data。

1.5. 创建Vue时, options可以放那些东西

p详细解析: https://cn.vuejs.org/v2/api/#%E9%80%89%E9%A1%B9-%E6%95%B0%E6%8D%AE

  • el:

    类型:string | HTMLElement

    作用:决定之后Vue实例会管理哪一个DOM

  • data:

    类型:Object | Function (组件当中data必须是一个函数)

    作用:Vue实例对应的数据对象

  • methods:

    类型:{ [key: string]: Function }

    作用:定义属于Vue的一些方法,可以在其他地方调用,也可以在指令中使用。

  • 生命周期函数

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fgQqOfuF-1641376421929)(C:\Users\你好啊\AppData\Roaming\Typora\typora-user-images\image-20210502215258637.png)]

二.插值语法

  • mustache(胡须)语法

     

    {{counter}}

  • v-once

  • {{counter}}

    该指令后面不需要跟任何表达式(比如之前的v-for后面是由跟表达式的)

    该指令表示元素和组件只渲染一次,不会随着数据的改变而改变。

  • v-html

    某些情况下,我们从服务器请求到的数据本身就是一个HTML代码

    如果我们直接通过{{}}来输出,会将HTML代码也一起输出。

    但是我们可能希望的是按照HTML格式进行解析,并且显示对应的内容。

    如果我们希望解析出HTML展示

    可以使用v-html指令

    该指令后面往往会跟上一个string类型

    会将string的html解析出来并且进行渲染

    <h2 v-html='link'>h2>
    
     data:{
           link:'百度一下'
         },
    
  • v-text

    v-text作用和Mustache比较相似:都是用于将数据显示在界面中

    v-text通常情况下,接受一个string类型

     

       data:{
          message:'你好啊,李银河',
       },
    
  • v-pre: {{}}

    v-pre用于跳过这个元素和它子元素的编译过程,用于显示原本的Mustache语法。

    比如下面的代码:

    第一个h2元素中的内容会被编译解析出来对应的内容

    第二个h2元素中会直接显示{{message}}

    <h2>{{message}}h2> 
    <h2 v-pre>{{message}}h2>    
    
      data:{
          message:'你好啊,李银河',
       },
    
  • v-cloak: 斗篷

  • 在某些情况下,我们浏览器可能会直接显然出未编译的Mustache标签。

 
 

{{message}}

<script>
  // 在vue解析之前, div中有一个属性v-cloak
  // 在vue解析之后, div中没有一个属性v-cloak
  setTimeout(function () {
    const app = new Vue({
      el: '#app',
      data: {
        message: '你好啊'
      }
    })
  }, 1000)
</script>

三. v-bind

主要作用是将值插入到我们模板的内容当中

  • 比如动态绑定a元素的href属性

  • 比如动态绑定img元素的src属性

  • 作用:动态绑定属性

  • 缩写::

  • 预期any (with argument) | Object (without argument)

  • 参数attrOrProp (optional)

3.1. v-bind绑定基本属性

  • v-bind:href

    <a v-bind:href='link'>百度a>
    
  • :src (v-bind缩写)

    <img :src='url' alt="">
    

3.2. v-bind动态绑定class

  • 对象语法

    对象语法的含义是:class后面跟的是一个对象。

    :class=’{类名: boolean}’

    用法一:直接通过{}绑定一个类

    <h2 :class="{'active': isActive}">Hello Worldh2>
    

    用法二:也可以通过判断,传入多个值

    <h2 :class="{'active': isActive, 'line': isLine}">Hello Worldh2>
    

    用法三:和普通的类同时存在,并不冲突

    注:如果isActive和isLine都为true,那么会有title/active/line三个类

    <h2 class="title" :class="{'active': isActive, 'line': isLine}">Hello Worldh2>
    

    用法四:如果过于复杂,可以放在一个methods或者computed中

    注:classes是一个计算属性

    <h2 class="title" :class="classes">Hello Worldh2>
    
  • 数组语法:

    数组语法的含义是:class后面跟的是一个数组。

    数组语法有下面这些用法:

    用法一:直接通过{}绑定一个类

    <h2 :class="['active']">Hello Worldh2>
    

    用法二:也可以传入多个值

    <h2 :class=“[‘active’, 'line']">Hello Worldh2>
    

    用法三:和普通的类同时存在,并不冲突

    注:会有title/active/line三个类

    <h2 class="title" :class=“[‘active’, 'line']">Hello Worldh2>
    

    用法四:如果过于复杂,可以放在一个methods或者computed中

    注:classes是一个计算属性

    <h2 class="title" :class="classes">Hello Worldh2>
    

3.3. v-bind动态绑定style

我们可以利用v-bind:style来绑定一些CSS内联样式。

在写CSS属性名的时候,比如font-size

我们可以使用驼峰式 (camelCase) fontSize

或短横线分隔 (kebab-case,记得用单引号括起来) ‘font-size’

  • 对象语法:

    :style="{color: currentColor, fontSize: fontSize + 'px'}"
    

    style后面跟的是一个对象类型

    对象的key是CSS属性名称

    对象的value是具体赋的值,值可以来自于data中的属性

  • 数组语法:

    <div v-bind:style="[baseStyles, overridingStyles]">div>
    

    style后面跟的是一个数组类型 多个值以,分割即可

四. 计算属性

我们知道,在模板中可以直接通过插值语法显示一些data中的数据。

但是在某些情况,我们可能需要对数据进行一些转化后再显示,或者需要将多个数据结合起来进行显示

比如我们有firstName和lastName两个变量,我们需要显示完整的名称。

但是如果多个地方都需要显示完整的名称,我们就需要写多个{{firstName}} {{lastName}}

我们可以将上面的代码换成计算属性:

OK,我们发现计算属性是写在实例的computed选项中的。

  • 案例一: firstName+lastName

       <div>{{name}}div>
    
    data:{
     firstName: "kobe",
      lastName: "blyant",
    }
    
      computed: {
                    name(){
                        return this.firstName + ' ' + this.lastName
                           },           
                }
    
  • 案例二: books -> price

<div>{{totalPrice}}div>
data:{
books:[
    {name:"百年孤独",price:50,count:2},
    {name:"霍乱时期的爱情",price:60,count:1},
    {name:"肖申克的救赎",price:50,count:2}
]
}
 computed: {
                name(){
                    return this.firstName + ' ' + this.lastName
                       },
                    totalPrice(){
                        return this.books.reduce((total,b) =>{//高阶函数
                            return total + b.price * b.count
                        },0)
                    }
                        
            }

五. 计算属性

5.1. 计算属性的本质

每个计算属性都包含一个getter和一个setter

在上面的例子中,我们只是使用getter来读取。

在某些情况下,你也可以提供一个setter方法(不常用)。

需要写setter的时候,代码如下

  • fullname: {set(), get()}

        fullName:{
                            get(){
                                console.log('调用了get函数');
                                return this.firstName +' '+ this.lastName
                            },
                            set(newValue){
                                console.log('调用了set函数');
                                const names=newValue.split('');
                                this.firstName=name[0]
                                this.lastName=name[1]
    
                            }
                        }
    

5.2. 计算属性和methods对比

  • 计算属性在多次使用时, 只会调用一次.

  • 它是有缓存的

    <div>{{getFullName()}}div>
    <div>{{getFullName()}}div>
    <div>{{getFullName()}}div>
    <div>-----------------div>
    <div>{{newFullName}}div>
    <div>{{newFullName}}div>
    <div>{{newFullName}}div>
    
     methods: {
             
             getFullName(){
             console.log('执行了methods方法');
             return this.firstName +' '+ this.lastName
    
              }
                },
                computed: {
                 newFullName(){
                 console.log('执行了计算属性');
                  return this.firstName +' '+ this.lastName
                  }       
                },
    
    结果: getFullName()打印了3次,newFullName()打印了1次

六. 事件监听

在前端开发中,我们需要经常和用于交互。

这个时候,我们就必须监听用户发生的时间,比如点击、拖拽、键盘事件等等

在Vue中如何监听事件呢?使用v-on指令

v-on****介绍

作用:绑定事件监听器

缩写:@

预期:Function | Inline Statement | Object

参数:event

6.1. 事件监听基本使用

v-on也有对应的语法糖:

v-on:click可以写成@click

<button v-on:click='sub'>-button>
<h2>{{counter}}h2>
<button @click='add'>+button>

6.2. 参数问题

当通过methods中定义方法,以供@click调用时,需要注意参数问题

情况一:如果该方法不需要额外参数,那么方法后的()可以不添加。

但是注意:如果方法本身中有一个参数,那么会默认将原生事件event参数传递进去

情况二:如果需要同时传入某个参数,同时需要event时,可以通过$event传入事件。

  • btnClick
  • btnClick(event)
  • btnClick(abc, event) -> $event
<button @click='handleAdd'>+1button>
<h2>{{counter}}h2>
<button @click='handleAddTen(10,$event)'>+10button>
   handleAdd(event){
   console.log(event);
   this.counter++
   },
   handleAddTen(count,event){
   console.log(event);
   this.counter+=10
   }

6.3. 修饰符

在某些情况下,我们拿到event的目的可能是进行一些事件处理。

Vue提供了修饰符来帮助我们方便的处理一些事件:

.stop - 调用 event.stopPropagation()。

.prevent - 调用 event.preventDefault()。

.{keyCode | keyAlias} - 只当事件是从特定键触发时才触发回调。

.native - 监听组件根元素的原生事件。

.once - 只触发一次回调。

  • stop

     
     <button @click.stop='doThis'>button>
    
  • prevent

    
    <button @click.prevent='doThis'>button>
    
  • .enter

     
     <input @keyup.enter='onEnter'>
     
     <input @keyup.13='onEnter'>
    
  • .once

    
    <button @click.once='doThis'>1button>
    
       
     <button @click.stop.prevent='doThis'>button>
    

七. 条件判断

v-if的原理:

v-if后面的条件为false时,对应的元素以及其子元素不会渲染。

也就是根本没有不会有对应的标签出现在DOM中

7.1. v-if/v-else-if/v-else

<div v-if='score>=90'><div v-else-if='score>=80
'score>=60 <div v-else> 不及格div>
 data: {
     score:89
 },

7.2. 登录小案例

<div v-if="type==='username'">
            <span>用户名span><input type="text" placeholder="请输入用户名">
        div>
        <div v-else="type==='email'">
            <span>邮箱span><input type="text" placeholder="请输入邮箱">
        div>
        <button @click='change'>切换类型button>
data: {
      type:"username" 
       },
       methods: {
        change(){
         this.type=this.type==="username" ? "email" : "username"
         }
            }

小问题:

如果我们在有输入内容的情况下,切换了类型,我们会发现文字依然显示之前的输入的内容。

但是按道理讲,我们应该切换到另外一个input元素中了。

在另一个input元素中,我们并没有输入内容。

为什么会出现这个问题呢?

问题解答:

这是因为Vue在进行DOM渲染时,出于性能考虑,会尽可能的复用已经存在的元素,而不是重新创建新的元素。

在上面的案例中,Vue内部会发现原来的input元素不再使用,直接作为else中的input来使用了。

解决方案:

如果我们不希望Vue出现类似重复利用的问题,可以给对应的input添加key

并且我们需要保证key的不同

<input type="text" placeholder="请输入用户名" key="username-input">
       
<input type="text" placeholder="请输入邮箱" key="username-email">

7.3. v-show

  • v-show和v-if区别

    v-show的用法和v-if非常相似,也用于决定一个元素是否渲染:

  • v-if和v-show对比

    v-if和v-show都可以决定一个元素是否渲染,那么开发中我们如何选择呢?

    v-if当条件为false时,压根不会有对应的元素在DOM中。

    v-show当条件为false时,仅仅是将元素的display属性设置为none而已。

  • 开发中如何选择呢?

    当需要在显示与隐藏之间切片很频繁时,使用v-show

    当只有一次切换时,通过使用v-if

     <div v-if='isShow'  @click='toggleShow'>v-ifdiv>
       <div v-show='isShow'>v-showdiv>
    
    toggleShow(){
          this.isShow=!this.isShow
          }
    

八. 循环遍历

当我们有一组数据需要进行渲染时,我们就可以使用v-for来完成。

v-for的语法类似于JavaScript中的for循环。

格式如下:

item in items。

如果在遍历的过程中不需要使用索引值

v-for=“movie in movies”

依次从movies中取出movie,并且在元素的内容中,我们可以使用Mustache语法,来使用movie

如果在遍历的过程中,我们需要拿到元素在数组中的索引值呢?

语法格式:v-for=(item, index) in items

其中的index就代表了取出的item在原数组的索引值

注意:cli3需要带:key=‘index’

8.1. 遍历数组

<ul>
    <li v-for="item in movies">{{item}}li>
    <div>---------------------------div>
    <li v-for='(movie,index) in movies' :key='index'>{{index}},{{movie}}li>
 ul>
data: {
          movies: ['星际穿越', '大话西游', '少年派', '盗梦空间'],
    },

8.2. 遍历对象

  • value
  • value, key
  • value, key, index
 <li v-for="(value,key,index) in info" >{{value}}--{{key}}--{{index}}li>
info:{
    name:'gm',
    age:24,
    height:160,
            }

官方推荐我们在使用v-for时,给对应的元素或组件添加上一个:key属性。

为什么需要这个key属性呢(了解)?

这个其实和Vue的虚拟DOM的Diff算法有关系。

这里我们借用React’s diff algorithm中的一张图来简单说明一下:

当某一层有很多相同的节点时,也就是列表节点时,我们希望插入一个新的节点

我们希望可以在B和C之间加一个F,Diff算法默认执行起来是这样的。

即把C更新成F,D更新成C,E更新成D,最后再插入E,是不是很没有效率?

所以我们需要使用key来给每个节点做一个唯一标识

Diff算法就可以正确的识别此节点

找到正确的位置区插入新的节点。

所以一句话,key的作用主要是为了高效的更新虚拟DOM

8.3. 数组哪些方法是响应式的

因为Vue是响应式的,所以当数据发生变化时,Vue会自动检测数据变化,视图会发生对应的更新。

Vue中包含了一组观察数组编译的方法,使用它们改变数组也会触发视图的更新。

操作数组:

push(),往后面添加元素

 this.movies.push('肖申克的救赎','我的姐姐');

pop(), 从后面开始删除元素

this.movies.pop();

shift() , 从前面添加元素

this.movies.unshift('肖申克的救赎','我的姐姐')

unshift(), 从前面开始删除元素

this.movies.shift()

splice()

splice作用: 删除元素/插入元素/替换元素

​ // 删除元素: 第二个参数传入你要删除几个元素(如果没有传,就删除后面所有的元素)

​ // 替换元素: 第二个参数, 表示我们要替换几个元素, 后面是用于替换前面的元素

​ // 插入元素: 第二个参数, 传入0, 并且后面跟上要插入的元素

this.letters.splice(1, 3, 'm', 'n', 'l', 'x')
this.letters.splice(1, 0, 'x', 'y', 'z')

sort()

排序数组,

this.books.sort((a,b) => {
return a.id-b.id;//升序
 return b.id-a.id;//降序
})

reverse(),反向数组

this.movies.reverse();
  <li v-for="item in movies">{{item}}li>
  <button @click='updata'>修改button>
    <div>---------------------------div>
  <li v-for="(item,index) in books">{{index}},{{item}}li>
data: {
                movies: ['1,星际穿越', '2,大话西游', '3,少年派', '4,盗梦空间'],
                books:[
                    {id:0,name:"百年孤独",price:50,count:2},
                    {id:1,name:"霍乱时期的爱情",price:60,count:1},
                    {id:3,name:"肖申克的救赎1",price:50,count:3},
                    {id:2,name:"肖申克的救赎2",price:50,count:3}
                ]
            },

注意: 通过索引值修改数组中的元素

   this.letters[0] = 'bbbbbb';

  this.letters.splice(0, 1, 'bbbbbb')

​ set(要修改的对象, 索引值, 修改后的值)

    Vue.set(this.letters, 0, 'bbbbbb')

五. 书籍案例

    <div id="app">

<table>
    <thead>
    <tr>
        <th>th>
        <th>书籍名称th>
        <th>出版日期th>
        <th>价格th>
        <th>购买数量th>
        <th>操作th>
    thead>
    <tbody>
        <tr v-for="(item,index) in books" :key="index">
            <td>{{item.id}}td>
            <td>{{item.name}}td>
            <td>{{item.date}}td>
            <td>{{item.price}}td>
            <td>
                <button v-bind:disabled="item.count <= 1" @click='sub(index)'>-button>

                <span>{{item.count}}span>
                <button @click='add(index)'>+button>
            td>
            <td><button @click='del(index)'>移除button>td>
        tr>   

    tbody>

    tr>
   

table>
<h2>{{ totalPrice | shoewPrice}}h2>

    div>


let vm=new Vue({
        el:"#app",

        data:{
            num:1,
            // total:0,
            status:false,
      books: [
      {
        id: 1,
        name: '《算法导论》',
        date: '2006-9',
        price: 85.00,
        count: 1
      },
      {
        id: 2,
        name: '《UNIX编程艺术》',
        date: '2006-2',
        price: 59.00,
        count: 1
      },
      {
        id: 3,
        name: '《编程珠玑》',
        date: '2008-10',
        price: 39.00,
        count: 1
      },
      {
        id: 4,
        name: '《代码大全》',
        date: '2006-3',
        price: 128.00,
        count: 1
      },
    ]

        }
,
methods:{
    add(index){
        this.books[index].count++
    },
    sub(index){
        this.books[index].count--
    },
    del(index){
        this.books.splice(index,1);
    }
},
computed:{
    totalPrice(){
       let total=0
         for(let i=0; i<this.books.length;i++){
           total+=this.books[i].price * this.books[i].count
        }   
        return total
     }    
},
filters:{
    shoewPrice(price){
        return  "¥" + price.toFixed(2)
    }
}

      })  

六. v-model的使用

表单控件在实际开发中是非常常见的。特别是对于用户信息的提交,需要大量的表单。

Vue中使用v-model指令来实现表单元素和数据的双向绑定。

案例的解析:

当我们在输入框输入内容时

因为input中的v-model绑定了message,所以会实时将输入的内容传递给message,message发生改变。

当message发生改变时,因为上面我们使用Mustache语法,将message的值插入到DOM中,所以DOM会发生响应的改变。

  • 所以,通过v-model实现了双向的绑定。

当然,我们也可以将v-model用于textarea元素

6.1. v-model的基本使用

 <input v-model='message' type="text" placeholder="">
    <h2>{{message}}h2>
data:{
  message:'',
} 
  • v-model = v-bind:value v-on:input

     <input v-model='message' type="text" placeholder="">
        
        <input type="text"
        v-bind:value='message' v-on:input="message = $event.target.value"
        >
        <h2>{{message}}h2>
    

6.2. v-model和radio/checkbox/select

  • ​ radio

       <label for="male">
            <input type="radio" :value='abc' v-model="gender" id="male">label>
    
        <label for="famale">
            <input type="radio" :value='def' v-model="gender" id="male">label>
        <span>你的选择:{{gender}}span>
    
       data:{
            gender:'',
            abc:"男生",
            def:'女生'
        }  
    
  • checkbox

    复选框分为两种情况:单个勾选框和多个勾选框

    单个勾选框:

    v-model即为布尔值。

    此时input的value并不影响v-model的值。

    多个复选框:

    当是多个复选框时,因为可以选中多个,所以对应的data中属性是一个数组。

    当选中某一个时,就会将input的value添加到数组中。

     <label for="check">
            <input  type='checkbox' v-model='checked' id="check">同意协议
        label>
        <p>是否选中:{{checked}}p>
    
        
        <label for="checkbox" ><input type="checkbox" v-model='hobbiles' value='篮球' >篮球 label>
        <label for="checkbox" ><input type="checkbox" v-model='hobbiles' value='足球' >足球 label>
        <label for="checkbox" ><input type="checkbox" v-model='hobbiles' value='排球' >排球 label>
        <p>是否选中:{{hobbiles}}p>
    
    checked:false,
    hobbiles:[]
    
  • select

    
        <select name="" id="" v-model='mySelect'>
            <option value="apple">苹果option>
            <option value="orange">橘子option>
            <option value="banana">香蕉option>
        select>
        <h2>你选择的水果是:{{mySelect}}h2>
    
        
        <select name="" id="" v-model='mySelects' multiple>
            <option value="apple">苹果option>
            <option value="orange">橘子option>
            <option value="banana">香蕉option>
        select>
        <h2>你选择的水果是:{{mySelects}}h2>
    
     mySelect:'apple',
     mySelects:[],
    

6.3. 修饰符

  • lazy

    lazy修饰符可以让数据在失去焦点或者回车时才会更新:

     <input type="text" v-model.lazy="message1">
      <h2>{{message1}}h2>
    
  • number

    默认情况下,在输入框中无论我们输入的是字母还是数字,都会被当做字符串类型进行处理。
    但是如果我们希望处理的是数字类型,那么最好直接将内容当做数字处理。
    number修饰符可以让在输入框中输入的内容自动转成数字类型:

  •  <input type="number" v-model="message2">
      <h2>{{message2}}-{{typeof message2}}h2>
    
  • trim

    如果输入的内容首尾有很多空格,通常我们希望将其去除

    ptrim修饰符可以过滤内容左右两边的空格

      <input type="text" v-model.trim="message3">
      <h2>{{message3}}h2>
    
     data: {
        	message1: '',
    	    message2: 0,
            message3: ''
        }
    

七. 组件化开发

7.1. 认识组件化

人面对复杂问题的处理方式:

任何一个人处理信息的逻辑能力都是有限的

所以,当面对一个非常复杂的问题时,我们不太可能一次性搞定一大堆的内容。

但是,我们人有一种天生的能力,就是将问题进行拆解。

如果将一个复杂的问题,拆分成很多个可以处理的小问题,再将其放在整体当中,你会发现大的问题也会迎刃而解。

组件化也是类似的思想:

如果我们将一个页面中所有的处理逻辑全部放在一起,处理起来就会变得非常复杂,而且不利于后续的管理以及扩展。

但如果,我们讲一个页面拆分成一个个小的功能块,每个功能块完成属于自己这部分独立的功能,那么之后整个页面的管理和维护就变得非常容易了。

  • 组件化是Vue.js中的重要思想

    它提供了一种抽象,让我们可以开发出一个个独立可复用的小组件来构造我们的应用。

    任何的应用都会被抽象成一颗组件树。

  • 组件化思想的应用:

    有了组件化的思想,我们在之后的开发中就要充分的利用它。

    尽可能的将页面拆分成一个个小的、可复用的组件。

    这样让我们的代码更加方便组织和管理,并且扩展性也更强。

    所以,组件是Vue开发中,非常重要的一个篇章,要认真学习。

7.2. 组件的基本使用

组件的使用分成三个步骤:

  1. 创建组件构造器
  2. 注册组件
  3. 使用组件。

我们来看看通过代码如何注册组件

查看运行结果:

和直接使用一个div看起来并没有什么区别。

但是我们可以设想,如果很多地方都要显示这样的信息,我们是不是就可以直接使用来完成呢?


<div id="app">
  <my-cpn>my-cpn>
  <my-cpn>my-cpn>
div>
  <my-cpn>my-cpn>
<script>
  // 1.创建组件构造器
  const cpn = Vue.extend({
    template: `
      

组件标题

我是组件的内容

`
}) // 2.注册组件(全局注册),并定义名称 Vue.component('my-cpn', cpn) const app = new Vue({ el: '#app', data: { } })
script>

1.Vue.extend():

调用Vue.extend()创建的是一个组件构造器。

通常在创建组件构造器时,传入template代表我们自定义组件的模板。

该模板就是在使用到组件的地方,要显示的HTML代码。

事实上,这种写法在Vue2.x的文档中几乎已经看不到了,它会直接使用下面我们会讲到的语法糖,但是在很多资料还是会提到这种方式,而且这种方式是学习后面方式的基础。

2.Vue.component():

调用Vue.component()是将刚才的组件构造器注册为一个组件,并且给它起一个组件的标签名称。

所以需要传递两个参数:1、注册组件的标签名 2、组件构造器

3.组件必须挂载在某个Vue实例下,否则它不会生效。

我们来看下面我使用了三次

而第三次其实并没有生效

7.3. 全局组件和局部组件

当我们通过调用Vue.component()注册组件时,组件的注册是全局的

这意味着该组件可以在任意Vue示例下使用。

如果我们注册的组件是挂载在某个实例中, 那么就是一个局部组件

  • 全局组件

    <div id="app1">
      <cpn>cpn>
    div>
    <div id="app2">
      <cpn>cpn>
    div>
    <cpn>cpn>
    <script>
      const cpn = Vue.extend({
        template: `
        

    我是标题

    我是组件中的内容

    `
    }) Vue.component('cpn', cpn) const app1 = new Vue({ el: '#app1' }) const app2 = new Vue({ el: '#app2' })
    script>
  • 局部组件

    <div id="app">
      <cpn>cpn>
    div>
    <div id="app2">
      <cpn>cpn>
    div>
    <script>
    	const cpn = Vue.extend({
    		template: `
        

    我是标题

    我是组件中的内容

    `
    }) const app = new Vue({ el: '#app', components: { 'cpn': cpn } }) const app2 = new Vue({ el: '#app2' })
    script>

7.4. 父组件和子组件

组件和组件之间存在层级关系

而其中一种非常重要的关系就是父子组件的关系

我们来看通过代码如何组成的这种层级关系:

父子组件错误用法:以子标签的形式在Vue实例中使用

因为当子组件注册到父组件的components时,Vue会编译好父组件的模块

该模板的内容已经决定了父组件将要渲染的HTML(相当于父组件中已经有了子组件中的内容了)

是只能在父组件中被识别的。

类似这种用法,是会被浏览器忽略的。

<div id="app">
  <cpn2>cpn2>
  
  <cpn1>cpn1>
div>
<script>
  // 1.注册一个子组件
  const cpn1 = Vue.extend({
    template: `
      

我是cpn1的标题

我是cpn1的内容,哈哈哈

`
}) // 2.注册一个父组件 const cpn2 = Vue.extend({ template: `

我是cpn2的标题

我是cpn2的内容,哈哈哈

`
, components: { 'cpn1': cpn1 } }) const app = new Vue({ el: '#app', components: { 'cpn2': cpn2 } })
script>

7.5. 注册的语法糖

在上面注册组件的方式,可能会有些繁琐。

Vue为了简化这个过程,提供了注册的语法糖。

主要是省去了调用Vue.extend()的步骤,而是可以直接使用一个对象来代替。

语法糖注册全局组件和局部组件:
	/**
   * 1.注册全局组件的语法糖
	 */ 
Vue.component('cpn', {
	  template: `
      <div>
        <h2>我是cpn的标题h2>
        <p>我是cpn的内容,哈哈哈p>
      div>
    `
  })
      
 const app = new Vue({
    el: '#app',
    //注册局部组件
    components:{
        'cpn1':{
            template:`
            
这是my-cpn1组件
`
} }, data: { } })

7.6. 模板的分类写法

我们通过语法糖简化了Vue组件的注册过程,另外还有一个地方的写法比较麻烦,就是template模块写法。

如果我们能将其中的HTML分离出来写,然后挂载到对应的组件上,必然结构会变得非常清晰。

Vue提供了两种方案来定义HTML模块内容:

使用

使用标签

  • script
<div id="app">
  <cpn1>cpn1>
div>
<script type="text/x-template" id="mycpn1">
    <div>我是script创建的</div>
script>
<script>
 
  const app = new Vue({
    el: '#app',
    components:{
        'cpn1':{
            template:'#mycpn1'
        },
    },

  })
script>

  • template
<div id="app">
  <cpn2>cpn2>
div>
<template id="mycpn2">
    <div>我是template创建的div>
template>
<script>
  const app = new Vue({
    el: '#app',
    components:{
        'cpn2':{
            template:'#mycpn2'
        }
    },
  })
script>

7.7. 数据的存放

组件是一个单独功能模块的封装:

这个模块有属于自己的HTML模板,也应该有属性自己的数据data。

组件中的数据是保存在哪里呢?顶层的Vue实例中吗?

我们先来测试一下,组件中能不能直接访问Vue实例中的data

  • 子组件不能直接访问父组件
<div id="app">
  <cpn2>cpn2>
div>
<template id="mycpn2">
    <div>{{message}}div>
template>
<script>
  const app = new Vue({
    el: '#app',
    components:{
        'cpn2':{
            template:'#mycpn2'
        }
    },
    data:{
    message:"我是data"
}
  })
script>

我们发现不能访问,而且即使可以访问,如果将所有的数据都放在Vue实例中,Vue实例就会变的非常臃肿。

结论:Vue组件应该有自己保存数据的地方。
  • 子组件中有自己的data, 而且必须是一个函数.

  • 组件自己的数据存放在哪里呢?

组件对象也有一个data属性(也可以有methods等属性,下面我们有用到)

只是这个data属性必须是一个函数

而且这个函数返回一个对象,对象内部保存着数据

<div id="app">
  <h2>{{message}}h2>
  <cpn>cpn>
div>
<template id="cpn">
  <div>
    <h2>组件标题h2>
    <p>组件的内容: {{message}}p>
  div>
template>
<script>
  const app = new Vue({
    el: '#app',
    data: {
    	message: '今天天气不错!'
    },
    components: {
    	'cpn': {
    		template: '#cpn',
        data() {
    			return {
    				message: '组件中的天气不错!'
          }
        }
      }
    }
  })

  • 为什么必须是一个函数.

    为什么data在组件中必须是一个函数呢?

    1.首先,如果不是一个函数,Vue直接就会报错。

    2.其次,原因是在于Vue让每个组件对象都返回一个新的对象,因为如果是同一个对象的,组件在多次使用后会相互影响。

    <div id="app">
      <cpn>cpn>
      <cpn>cpn>
    div>
    
    <template id="cpn">
      <div>
        <h2>{{counter}}h2>
        <button @click="btnClick">按钮button>
      div>
    template>
    <script>
      const obj = {
      	counter: 0
      }
      const app = new Vue({
        el: '#app',
        components: {
        	'cpn': {
        		template: '#cpn',
            data() {
        			return {
        				counter: 0
              }
            },
            methods: {
        			btnClick() {
        				this.counter++
              }
            }
          }
        }
      })
    script>
    

7.8. 父子组件的通信

我们提到了子组件是不能引用父组件或者Vue实例的数据的。

但是,在开发中,往往一些数据确实需要从上层传递到下层:

比如在一个页面中,我们从服务器请求到了很多的数据。

其中一部分数据,并非是我们整个页面的大组件来展示的,而是需要下面的子组件进行展示。

这个时候,并不会让子组件再次发送一个网络请求,而是直接让大组件**(父组件)将数据传递给小组件(子组件)**。

如何进行父子组件间的通信呢?Vue官方提到

通过props向子组件传递数据

通过事件向父组件发送消息

parent(父组件)—(通过 props)–> child(子组件)

child(子组件)—(通过 $emit Events)–> parent(父组件)

在下面的代码中,我直接将Vue实例当做父组件,并且其中包含子组件来简化代码。

真实的开发中,Vue实例和子组件的通信和父组件和子组件的通信过程是一样的。

  • 父传子: props

    <div id="app">
      <child-cpn :message='message1'>child-cpn>
    div>
    <template id="cpn">
      <div>
        <h2>组件标题h2>
        <p>组件的内容: {{message}}p>
      div>
    template>
    <script>
      const app = new Vue({
        el: '#app',
        data: {
        	message1: '今天天气不错!'
        },
        components: {
        	'childCpn': {
        		template: '#cpn',
            props:['message'],
          }
        }
      })
    script>
    

    上面我们的props选项是使用一个数组。

    我们说过,除了数组之外,我们也可以使用对象,当需要对props进行类型等验证时,就需要对象写法了。

    验证都支持哪些数据类型呢?

    1.String

    2.Number

    3.Boolean

    4.Array

    6.Object

    7.Date

    8.Function

    9.Symbol

    当我们有自定义构造函数时,验证也支持自定义的类型

    
      <script>
    	class Proson(){
    		consttuctor(name,age){
    			this.name=name,
    			this.age=age
    			  }
    		toString(){
    			return `姓名:${this.name}  年龄:${this.age}`
    			  }
    
    	  }
        vue.components('my-component', {
          props: {
            //基础的类型检查,(null匹配任何类型)
            propA: Number,
            //可能多个类型
            propB: [Number, String],
            //必填的字符串
            propC: {
              type: String,
              required: true,
            },
            //带有默认的数字
            propD: {
              type: Number,
              default: 100,
            },
            //带有默认的对象	    
            propE: {
              type: Objetc,
              //对象的默认值必须从工厂函数获取,
              default: function () {
                return { message: 'hello' }
              }
            },
            //自定义验证函数
            propF: {
              validator: function (value) {
                //这个字必须匹配下列字符串中的一个
                return ['success', 'wraning', 'danger'].indexOf(value) !== -1
              }
            },
    	propG:{
    		type:Person,
    		  }
          }
      })
      </script>
    
  • 子传父: $emit

    props用于父组件向子组件传递数据,还有一种比较常见的是子组件传递数据或事件到父组件中。

    我们应该如何处理呢?这个时候,我们需要使用自定义事件来完成。

    什么时候需要自定义事件呢?

    当子组件需要向父组件传递数据时,就要用到自定义事件了。

    我们之前学习的v-on不仅仅可以用于监听DOM事件,也可以用于组件间的自定义事件。

    自定义事件的流程:

    在子组件中,通过$emit()来触发事件。

    在父组件中,通过v-on来监听子组件事件。

    我们来看一个简单的例子:

    我们之前做过一个两个按钮+1和-1,点击后修改counter。

    我们整个操作的过程还是在子组件中完成,但是之后的展示交给父组件。

    这样,我们就需要将子组件中的counter,传给父组件的某个属性,比如total。

    DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>子级向父级传递数据title>
    head>
    <body>
    <div id="app">
        //2.父组件接受用changeTotal函数接受increment和decrement的事件
        <child-cpn @increment='changeTotal' @decrement='changeTotal'>child-cpn>
        <h3>{{total}}h3>
    div>
    <template id="child">
        <div>
            <button @click='decrement'>-button>
            <button @click='increment'>+button>
        div>
    template>
    <script src="./js/vue.js">script>
    <script>
        let vm=new Vue({
            el:"#app",
            data:{
                total:0,
            },
            methods:{
                //3.处理事件和接受参数counter
                changeTotal(counter){
                    this.total=counter
                }
            },
            components:{
                'child-cpn':{
                    template:"#child",
                    data(){
                        return{
                            counter:0
                        }
                    },
                    methods:{
                        increment(){
                            this.counter++;
                            //1.给父组件传递事件(increment)和参数(counter)
                            this.$emit('increment',this.counter)
                        },
                        decrement(){
                            this.counter--;
                             //1.给父组件传递事件(increment)和参数(counter)
                            this.$emit('decrement',this.counter)
                        }
                    }
                }
            }
        })
    script>
    body>
    html>
    
  • 父子组件的访问方式: $children

      <div id="app">
            <child-cpna>child-cpna>
            <child-cpnb>child-cpnb>
            <button @click='showChild'>显示button>
        div>
        <template id="child1">
            <div>我是子组件信息1div>
        template>
        <template id="child2">
            <div>我是子组件信息2div>
        template>
        <script>
            let vm = new Vue({
                el: "#app",
                methods: {
                    showChild() {
                        // console.log(this.$children.message);
                        for( let i in this.$children){
                            const cpn=this.$children[i].message
                            console.log(cpn);
                        }
                    }
                },
               components: {
                    'child-cpna': {
                        template: "#child1",
                        data() {
                            return {
                                message: "我是组件1的message"
                            }
                        }
                    },
                    'child-cpnb': {
                        template: "#child2",
                        data() {
                            return {
                                message: "我是组件2的message"
                            }
                        }
                    }
                }
            })
        script>
    
    
  • 父子组件的访问方式: $refs

    $children的缺陷:

    通过$children访问子组件时,是一个数组类型,访问其中的子组件必须通过索引值。

    但是当子组件过多,我们需要拿到其中一个时,往往不能确定它的索引值,甚至还可能会发生变化。

    有时候,我们想明确获取其中一个特定的组件,这个时候就可以使用$refs

    $refs的使用:

    $refs和ref指令通常是一起使用的。

    首先,我们通过ref给某一个子组件绑定一个特定的ID。

    其次,通过this.$refs.ID就可以访问到该组件了。

          <child-cpna ref='child1'>child-cpna>
            <child-cpnb ref='child2'>child-cpnb>
            <button @click='showRefsCpn'>通过refs访问子组件button>
            
    
       methods: {
                    showRefsCpn() {
                        console.log(this.$refs.child1.message);
                        console.log(this..child2.message);
                    }
                },
    
  • 父子组件的访问方式: $parent

     this.$parent.message
    
  • 非父子组件通信

    刚才我们讨论的都是父子组件间的通信,那如果是非父子关系呢?

    非父子组件关系包括多个层级的组件,也包括兄弟组件的关系。

    在Vue1.x的时候,可以通过 d i s p a t c h 和 dispatch和 dispatchbroadcast完成

    $dispatch用于向上级派发事件

    $broadcast用于向下级广播事件

    但是在Vue2.x都被取消了

    在Vue2.x中,有一种方案是通过中央事件总线,也就是一个中介来完成。

    但是这种方案和直接使用Vuex的状态管理方案还是逊色很多。

    并且Vuex提供了更多好用的功能,所以这里我们暂且不讨论这种方案,后续我们专门学习Vuex的状态管理。

7.9 slot

slot翻译为插槽:

在生活中很多地方都有插槽,电脑的USB插槽,插板当中的电源插槽。

插槽的目的是让我们原来的设备具备更多的扩展性。

比如电脑的USB我们可以插入U盘、硬盘、手机、音响、键盘、鼠标等等。

组件的插槽:

组件的插槽也是为了让我们封装的组件更加具有扩展性。

让使用者可以决定组件内部的一些内容到底展示什么。

栗子:移动网站中的导航栏。

移动开发中,几乎每个页面都有导航栏。

导航栏我们必然会封装成一个插件,比如nav-bar组件。

一旦有了这个组件,我们就可以在多个页面中复用了。

但是,每个页面的导航是一样的吗?No

slot基本使用

了解了为什么用slot,我们再来谈谈如何使用slot?

在子组件中,使用特殊的元素就可以为子组件开启一个插槽。

该插槽插入什么内容取决于父组件如何使用。

我们通过一个简单的例子,来给子组件定义一个插槽:

中的内容表示,如果没有在该组件中插入任何其他内容,就默认显示该内容

有了这个插槽后,父组件如何使用呢?

<div id="app">
    <child-cpn>
        <div>我是替代的插槽div>
    child-cpn>
div>
<template id="child">
 <div> <slot>我是默认的插槽slot>div>
template>
<script>
    let vm=new Vue({
        el:"#app",
        data:{
        },
        components:{
            'child-cpn':{
                template:"#child",
            }
        }
    })
script>

具名插槽slot

    <child-cpn>
     
        <template v-slot:header>
            <h1>Here might be a page titleh1>
          template>
        
          <template v-slot:footer>
            <p>Here's some contact infop>
          template>
    child-cpn>
    
    <template id="child">
     <div>
     
            <div>
              <slot name="header">slot>
            div>
            <div>
              <slot name="footer">slot>
            div>
          div>


template>
    
  • 作用域插槽:准备

作用域插槽是slot一个比较难理解的点,而且官方文档说的又有点不清晰。

这里,我们用一句话对其做一个总结,然后我们在后续的案例中来体会:

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

我们先提一个需求:

子组件中包括一组数据,比如:pLanguages: [‘JavaScript’, ‘Python’, ‘Swift’, ‘Go’, ‘C++’]

需要在多个界面进行展示:

某些界面是以水平方向一一展示的,

某些界面是以列表形式展示的,

某些界面直接展示一个数组

内容在子组件,希望父组件告诉我们如何展示,怎么办呢?

利用slot作用域插槽就可以了

我们来看看子组件的定义:

<template id="child">
       <div>
              <slot :data='pLanguages'>slot>
          div>

template>
  components:{
            'child-cpn':{
                template:"#child",
                data(){
                    return{
                        pLanguages: ['JavaScript', 'Python', 'Swift', 'Go', 'C++']
                    }
                },
            }
        }

作用域插槽:使用

在父组件使用我们的子组件时,从子组件中拿到数据:

我们通过获取到slotProps属性

在通过slotProps.data就可以获取到刚才我们传入的data了

<div id="app">
    <child-cpn>
        <template slot-scope="slotProps">
            <ul>
                <li v-for='(item,index) in slotProps.data' :key="index">{{item}}li>
            ul>
        template>
    child-cpn>
div>

<template id="child">
       <div>
              <slot :data='pLanguages'>slot>
          div>

template>

    let vm=new Vue({
        el:"#app",
        components:{
            'child-cpn':{
                template:"#child",
                data(){
                    return{
                        pLanguages: ['JavaScript', 'Python', 'Swift', 'Go', 'C++']
                    }
                },
            }
        }
    })

八,模块化开发

  • JavaScript原始功能

    在网页开发的早期,js制作作为一种脚本语言,做一些简单的表单验证或动画实现等,那个时候代码还是很少的。

    那个时候的代码是怎么写的呢?直接将代码写在

<script>

        var moduleA=(function(){
            var obj={

            }
            obj.flag=true
            obj.myFunction=function(info) {
                console.log(info);
            }
            return obj
        })()

        if(moduleA.flag){
            console.log('小明是天才');
        }
        moduleA.myFunction('小明长的漂亮')

    </script>
CommonJS(了解)
  • 导入与导出
  //CommonJS的导出
 module.exports={
            flag:true,
            test(a,b){
                return a+b
            },
            demo(a,b){
                return a-b
            }
        }
        //CommonJS的导入
        let {test,demo,flag }=require("moduleA");

        let _mA=require("moduleA");
        let test=_mA.test
        let demo=_mA.demo
        let flag=_mA.flag
export(导出)的使用
export let name ='gm'
export let age ='18'
export let height ='167'

//另一种写法
 let name ='gm'
 let age ='18'
 let height ='167'
 export {name,age,height}
  • 导出函数或类

 export  function test(content){
    console.log(content);
 }
 export class Person{
     constructor(){
        this.name=name,
        this.age=age

       
     }
     eat(){
    console.log(this.name+'在吃饭');
    }
 }
 export {test ,Person}
  • export default

某些情况下,一个模块中包含某个的功能,我们并不希望给这个功能命名,而且让导入者可以自己来命名

这个时候就可以使用export default

 export default function(object){
     console.log(object)
 }

另外,需要注意

export default在同一个模块中,不允许同时存在多个。

import使用

我们使用export指令导出了模块对外提供的接口,下面我们就可以通过import命令来加载对应的这个模块了

首先,我们需要在HTML代码中引入两个js文件,并且类型需要设置为module

  

import指令用于导入模块中的内容,比如main.js的代码

 import {name,age,height} from './js/main.js';
          console.log(name);

如果我们希望某个模块中所有的信息都导入,一个个导入显然有些麻烦:

通过*可以导入模块中所有的export变量

但是通常情况下我们需要给*起一个别名,方便后续的使用

 import as info from './js/main.js';
          console.log(info.name,info.age,info.height);

九,webpack

内容概述

认识webpack

webpack的安装

webpack的起步

webpack的配置

loader的使用

webpack中配置Vue

plugin的使用

搭建本地服务器

什么是Webpack?

这个webpack还真不是一两句话可以说清楚的。

我们先看看官方的解释:

At its core, webpack is a static module bundler for modern JavaScript applications.

从本质上来讲,webpack是一个现代的JavaScript应用的静态模块打包工具。

但是它是什么呢?用概念解释概念,还是不清晰。

我们从两个点来解释上面这句话:模块打包[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4EAeMlCc-1641376421932)(C:\文件\web\vue\vue-md-image\image-20210604201240609.png)]

  • 前端模块化:

    在前面学习中,我已经用了大量的篇幅解释了为什么前端需要模块化。

而且我也提到了目前使用前端模块化的一些方案:AMD、CMD、CommonJS、ES6。

在ES6之前,我们要想进行模块化开发,就必须借助于其他的工具,让我们可以进行模块化开发。

并且在通过模块化开发完成了项目后,还需要处理模块间的各种依赖,并且将其进行整合打包。

而webpack其中一个核心就是让我们可能进行模块化开发,并且会帮助我们处理模块间的依赖关系。

而且不仅仅是JavaScript文件,我们的CSS、图片、json文件等等在webpack中都可以被当做模块来使用(在后续我们会看到)。

这就是webpack中模块化的概念。

  • 打包如何理解呢?

理解了webpack可以帮助我们进行模块化,并且处理模块间的各种复杂关系后,打包的概念就非常好理解了。

就是将webpack中的各种资源模块进行打包合并成一个或多个包(Bundle)。

并且在打包的过程中,还可以对资源进行处理,比如压缩图片,将scss转成css,将ES6语法转成ES5语法,将TypeScript转成JavaScript等等操作。

但是打包的操作似乎grunt/gulp也可以帮助我们完成,它们有什么不同呢?

  • 和grunt/gulp的对比

    grunt/gulp的核心是Task

    我们可以配置一系列的task,并且定义task要处理的事务(例如ES6、ts转化,图片压缩,scss转成css)

    之后让grunt/gulp来依次执行这些task,而且让整个流程自动化。

    所以grunt/gulp也被称为前端自动化任务管理工具。

    我们来看一个gulp的task

    下面的task就是将src下面的所有js文件转成ES5的语法。

    并且最终输出到dist文件夹中。

    什么时候用grunt/gulp呢?

    如果你的工程模块依赖非常简单,甚至是没有用到模块化的概念。

    只需要进行简单的合并、压缩,就使用grunt/gulp即可。

    但是如果整个项目使用了模块化管理,而且相互依赖非常强,我们就可以使用更加强大的webpack了。

    所以,grunt/gulp和webpack有什么不同呢?

    grunt/gulp更加强调的是前端流程的自动化,模块化不是它的核心。

    webpack更加强调模块化开发管理,而文件压缩合并、预处理等功能,是他附带的功能。

webpack安装

安装webpack首先需要安装Node.js,Node.js自带了软件包管理工具npm

查看自己的node版本:

node -v

全局安装webpack(这里我先指定版本号3.6.0,因为vue cli2依赖该版本)

npm install [email protected] -g

局部安装webpack(后续才需要)

npm install [email protected] --save-dev

–save-dev是开发时依赖,项目打包后不需要继续使用的。

  • 为什么全局安装后,还需要局部安装呢?

在终端直接执行webpack命令,使用的全局安装的webpack

当在package.json中定义了scripts时,其中包含了webpack命令,那么使用的是局部webpack

webpack的使用

注意:

在package.json中,devDependencies下添加包名和版本使用npm install --devDependencies可以直接下载全部的包

 "devDependencies": {
    "webpack": "^3.6.0",
    "webpack-cli": "^4.7.0",
    "babel-core": "^6.26.3",
    "babel-loader": "^7.1.5",
    "babel-preset-es2015": "^6.24.1",
    "css-loader": "^2.0.2",
    "file-loader": "^3.0.1",
    "less": "^3.9.0",
    "less-loader": "^4.1.0",
    "style-loader": "^0.23.1",
    "url-loader": "^1.1.2"
  },
npm install --devDependencies
准备工作

我们创建如下文件和文件夹:

文件和文件夹解析:

dist文件夹:用于存放之后打包的文件

src文件夹:用于存放我们写的源文件

​ main.js:项目的入口文件。具体内容查看下面详情。

​ mathUtils.js:定义了一些数学工具函数,可以在其他地方引用,并且使用。具体内容查看下面的详情。

index.html:浏览器打开展示的首页html

package.json:通过npm init生成的,npm包管理的文件(暂时没有用上,后面才会用上)

mathUtils.js文件中的代码:

main.js文件中的代码:

js文件的打包

现在的js文件中使用了模块化的方式进行开发,他们可以直接使用吗?不可以。

因为如果直接在index.html引入这两个js文件,浏览器并不识别其中的模块化代码。

另外,在真实项目中当有许多这样的js文件时,我们一个个引用非常麻烦,并且后期非常不方便对它们进行管理。

我们应该怎么做呢?使用webpack工具,对多个js文件进行打包。

我们知道,webpack就是一个模块化的打包工具,所以它支持我们代码中写模块化,可以对模块化的代码进行处理。(如何处理的,待会儿在原理中,我会讲解)

另外,如果在处理完所有模块之间的关系后,将多个js打包到一个js文件中,引入时就变得非常方便了。

OK,如何打包呢?使用webpack的指令即可

webpack src/main.js dist/bundle.js   

"src/main.js"是将要打包文件的路径,"dist/bundle.js"是打包的目标文件

  • 使用打包后的文件

    打包后会在dist文件下,生成一个bundle.js文件

    文件内容有些复杂,这里暂时先不看,后续再进行分析。

    bundle.js.html中引入即可文件,是webpack处理了项目直接文件依赖后生成的一个js文件,我们只需要将这个js文件在index


  • 入口和出口

    我们考虑一下,如果每次使用webpack的命令都需要写上入口和出口作为参数,就非常麻烦,有没有一种方法可以将这两个参数写到配置中,在运行时,直接读取呢?

    当然可以,就是创建一个webpack.config.js文件

const path=require(‘path’);
module.exports={
//入口,可以是字符串,对象,数组,这是入口只有一个,所以用字符串九可以
entry:’./src/main.js’,
//出口,通常是一个对象,里面至少有个属性path和filenama
output:{
path:path.resolve(__dirname,‘dist’),//path是一个绝对路径
filname:“bundle.js”
}
}


- 局部安装webpack

目前,我们使用的webpack是全局的webpack,如果我们想使用局部来打包呢?

因为一个项目往往依赖特定的webpack版本,全局的版本可能很这个项目的webpack版本不一致,导出打包出现问题。

所以通常一个项目,都有自己局部的webpack。

第一步,项目中需要安装自己局部的webpack

这里我们让局部安装webpack3.6.0

Vue CLI3中已经升级到webpack4,但是它将配置文件隐藏了起来,所以查看起来不是很方便。

npm install [email protected] --save-dev


第二步,通过node_modules/.bin/webpack启动webpack打包

node_module/.bin/webpack


- package.json中定义启动

但是,每次执行都敲这么一长串有没有觉得不方便呢?

OK,我们可以在package.json的scripts中定义自己的执行脚本。

package.json中的scripts的脚本在执行时,会按照一定的顺序寻找命令对应的位置。

首先,会寻找本地的node_modules/.bin路径中对应的命令。

如果没有找到,会去全局的环境变量中寻找。

如何执行我们的build指令呢

```js
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build":"webpack"//使用npm run build 来启动打包数据
  },
什么是loader?

loader是webpack中一个非常核心的概念。

webpack用来做什么呢?

在我们之前的实例中,我们主要是用webpack来处理我们写的js代码,并且webpack会自动处理js之间相关的依赖。

但是,在开发中我们不仅仅有基本的js代码处理,我们也需要加载css、图片,也包括一些高级的将ES6转成ES5代码,将TypeScript转成ES5代码,将scss、less转成css,将.jsx、.vue文件转成js文件等等。

对于webpack本身的能力来说,对于这些转化是不支持的。

那怎么办呢?给webpack扩展对应的loader就可以啦。

loader使用过程:

步骤一:通过npm安装需要使用的loader

步骤二:在webpack.config.js中的modules关键字下进行配置

大部分loader我们都可以在webpack的官网中找到,并且学习对应的用法。

css文件处理 - 准备工作

项目开发过程中,我们必然需要添加很多的样式,而样式我们往往写到一个单独的文件中。

在src目录中,创建一个css文件,其中创建一个normal.css文件。

我们也可以重新组织文件的目录结构,将零散的js文件放在一个js文件夹中。

normal.css中的代码非常简单,就是将body设置为red

但是,这个时候normal.css中的样式会生效吗?

当然不会,因为我们压根就没有引用它。

webpack也不可能找到它,因为我们只有一个入口,webpack会从入口开始查找其他依赖的文件。

在入口文件中引用:

//main.js中引入:
require("./css/normal.css");

css文件处理 – 打包报错信息:

ERROR in ./src/css/normal.css
Module parse failed: C:\文件\web\vue\cli\test\stu-webpack\src\css\normal.css Unexpected token (1:1)
You may need an appropriate loader to handle this file type.
| p{
|     color: darkred;
| }
 @ ./src/main.js 5:0-27

这个错误告诉我们:加载normal.css文件必须有对应的loader。

  • css文件处理 – css-loader

    在webpack的官方中,我们可以找到如下关于样式的loader使用方法:

    npm install --save--dev css-loader
    

    file.js

    import css from 'file.css'
    

    按照官方配置webpack.config.js文件

       module:{
            rules:[
                {
                    test:/\.css$/,
                    use:['style-loader','css-loader'],
                }
            ]
        }
    

    注意:配置中有一个style-loader,我们并不知道它是什么,所以可以暂时不进行配置。

    重新打包项目:

    但是,运行index.html,你会发现样式并没有生效。

    原因是css-loader只负责加载css文件,但是并不负责将css具体样式嵌入到文档中。

    这个时候,我们还需要一个style-loader帮助我们处理。

  • css文件处理 – style-loader

    我们来安装style-loader

    npm install --save--dev style-loader
    

    注意:style-loader需要放在css-loader的前面。

    疑惑:不对吧?按照我们的逻辑,在处理css文件过程中,应该是css-loader先加载css文件,再由style-loader来进行进一步的处理,为什么会将style-loader放在前面呢?

    答案:这次因为webpack在读取使用的loader的过程中,是按照从右向左的顺序读取的。

    目前,webpack.config.js的配置如下:

    const path=require('path');
    
    module.exports={
        //入口,可以是字符串,对象,数组,这是入口只有一个,所以用字符串九可以
        entry:'./src/main.js',
        //出口,通常是一个对象,里面至少有个属性path和filenama
        output:{
        path:path.resolve(__dirname,'dist'),//path是一个绝对路径
        filename:"bundle.js"
    
        },
        module:{
            rules:[
                {
                    test:/\.css$/,
                    use:['style-loader','css-loader'],
                }
            ]
        }
    }
    
    less文件处理 – 准备工作

    如果我们希望在项目中使用less、scss、styles来写样式,webpack是否可以帮助我们处理呢?

    我们这里以less为例,其他也是一样的。

    我们还是先创建一个less文件,依然放在css文件夹中

    @fontSize:50px;
    @fontColor:red;
    
    body{
        font-size: @fontSize;
        color: @fontColor;
    }
    
    //main.js引入less文件
    require("./css/special.less");
    
    document.writeln('
    hello
    '
    )
    less文件处理 – less-loader

    继续在官方中查找,我们会找到less-loader相关的使用说明

    首先,还是需要安装对应的loader

    npm install --save--dev less-loader less 
    

    注意:我们这里还安装了less,因为webpack会使用less对less文件进行编译

    其次,修改对应的配置文件

    添加一个rules选项,用于处理.less文件

      rules:[
                {//处理css
                    test:/\.css$/,
                    use:['style-loader','css-loader'],
                },
                {//处理less
                    test:/\.less@/,
                    use:[{
                        loader:"style-loader"
                    },
                    {
                        loader:"css-loader"
                    },{
                        loader:"less-loader"
                    }
                ]
                }
            ]
    
图片文件处理 – 资源准备阶段

首先,我们在项目中加入两张图片:

一张较小的图片test01.jpg(小于8kb),一张较大的图片test02.jpeg(大于8kb)

待会儿我们会针对这两张图片进行不同的处理

我们先考虑在css样式中引用图片的情况,所以我更改了normal.css中的样式:

div{
    color: darkred;
    background: url('../img/test.jpg');
}

如果我们现在直接打包,会出现如下问题

ERROR in ./src/img/test.jpg
Module parse failed: C:\文件\web\vue\cli\test\stu-webpack\src\img\test.jpg Unexpected character '�' (1:0)
You may need an appropriate loader to handle this file type.
(Source code omitted for this binary file)
 @ ./node_modules/[email protected]@css-loader/dist/cjs.js!./src/css/normal.css 4:41-67
 @ ./src/css/normal.css
 @ ./src/main.js
图片文件处理 – url-loader

图片处理,我们使用url-loader来处理,依然先安装url-loader

cnpm install url-loader –D

修改webpack.config.js配置文件:

{
                test: /\.(png|jpg|gif|jpeg)$/,
                use: [
                  {
                    loader: 'url-loader',
                    options: {
                      // 当加载的图片, 小于limit时, 会将图片编译成base64字符串形式.
                      // 当加载的图片, 大于limit时, 需要使用file-loader模块进行加载.
                      limit: 13000,
                      name: 'img/[name].[hash:8].[ext]'
                    },
                  }
                ]
              },

再次打包,运行index.html,就会发现我们的背景图片选出了出来。

而仔细观察,你会发现背景图是通过base64显示出来的

OK,这也是limit属性的作用,当图片小于8kb时,对图片进行base64编码

图片文件处理 – file-loader

那么问题来了,如果大于8kb呢?我们将background的图片改成test02.jpg

这次因为大于8kb的图片,会通过file-loader进行处理,但是我们的项目中并没有file-loader

所以,我们需要安装file-loader

cnpm install file-loader –D

再次打包,就会发现dist文件夹下多了一个图片文件

  • 图片文件处理 – 修改文件名称

    我们发现webpack自动帮助我们生成一个非常长的名字

    这是一个32位hash值,目的是防止名字重复但是,真实开发中,我们可能对打包的图片名字有一定的要求

    比如,将所有的图片放在一个文件夹中,跟上图片原来的名称,同时也要防止重复

    所以,我们可以在options中添加上如下选项:

    img:文件要打包到的文件夹

    name:获取图片原来的名字,放在该位置

    hash:8:为了防止图片名称冲突,依然使用hash,但是我们只保留8位

    ext:使用图片原来的扩展名

    但是,我们发现图片并没有显示出来,这是因为图片使用的路径不正确

    默认情况下,webpack会将生成的路径直接返回给使用者

    但是,我们整个程序是打包在dist文件夹下的,所以这里我们需要在路径下再添加一个dist/

    entry: './src/main.js',
      output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'bundle.js',
        publicPath: 'dist/'   //dist下面添加文件夹
      },
    
less处理(没配置成功)
npm install style-resources-loader vue-cli-plugin-style-resources-loader less-loader less -S

vue.config.js文件中的配置


const path = require('path');
module.exports = {
pluginOptions: {
    'style-resources-loader': {
      preProcessor: 'less',
      patterns: [
        path.resolve(__dirname, './src/assets/common/global.less')
      ]
    }
  }
}
ES6语法处理

如果你仔细阅读webpack打包的js文件,发现写的ES6语法并没有转成ES5,那么就意味着可能一些对ES6还不支持的浏览器没有办法很好的运行我们的代码。

在前面我们说过,如果希望将ES6的语法转成ES5,那么就需要使用babel。

而在webpack中,我们直接使用babel对应的loader就可以了。

cnpm install --save--dev babel-core babel-preset-es2015
cnpm i babel-loader -D

webpack.config.json

    {//es6转换成es5
                test: /\.js$/,
                // exclude: 排除
                // include: 包含
                exclude: /(node_modules|bower_components)/,
                use: {
                  loader: 'babel-loader',
                  options: {
                    presets: ['es2015']
                  }
                }
              }

重新打包,查看bundle.js文件,发现其中的内容变成了ES5的语法

引入vue.js
cnpm install vue --save

那么,接下来就可以按照我们之前学习的方式来使用Vue了

打包项目 – 错误信息

修改完成后,重新打包,运行程序:

打包过程没有任何错误(因为只是多打包了一个vue的js文件而已)

但是运行程序,没有出现想要的效果,而且浏览器中有报错

bundle.js:938 [Vue warn]: You are using the runtime-only build of Vue where the template compiler is not available. Either pre-compile the templates into render functions, or use the compiler-included build.
(found in )

这个错误说的是我们使用的是runtime-only版本的Vue,什么意思呢?

这里我只说解决方案:Vue不同版本构建,后续我具体讲解runtime-only和runtime-compiler的区别。

所以我们修改webpack的配置,添加如下内容即可(与module同级)

  module:{
    },
    resolve: {
        // alias: 别名
        extensions: ['.js', '.css', '.vue'],
        alias: {
          'vue$': 'vue/dist/vue.esm.js'
        }
      }
el和template区别(一)

正常运行之后,我们来考虑另外一个问题:

如果我们希望将data中的数据显示在界面中,就必须是修改index.html

如果我们后面自定义了组件,也必须修改index.html来使用组件

但是html模板在之后的开发中,我并不希望手动的来频繁修改,是否可以做到呢?

定义template属性:

在前面的Vue实例中,我们定义了el属性,用于和index.html中的#app进行绑定,让Vue实例之后可以管理它其中的内容

这里,我们可以将div元素中的{{message}}内容删掉,只保留一个基本的id为div的元素

但是如果我依然希望在其中显示{{message}}的内容,应该怎么处理呢?

我们可以再定义一个template属性,代码如下:

import Vue from 'vue'
new Vue({
    el:'#app',
    template:"
{{name}}
"
, data:{ name:"gm" } })
el和template区别(二)

重新打包,运行程序,显示一样的结果和HTML代码结构

那么,el和template模板的关系是什么呢?

在我们之前的学习中,我们知道el用于指定Vue要管理的DOM,可以帮助解析其中的指令、事件监听等等。

而如果Vue实例中同时指定了template,那么template模板的内容会替换掉挂载的对应el的模板。

这样做有什么好处呢?

这样做之后我们就不需要在以后的开发中再次操作index.html,只需要在template中写入对应的标签即可

但是,书写template模块非常麻烦怎么办呢?

没有关系,稍后我们会将template模板中的内容进行抽离。

会分成三部分书写:template、script、style,结构变得非常清晰。

Vue组件化开发引入

在学习组件化开发的时候,我说过以后的Vue开发过程中,我们都会采用组件化开发的思想。

那么,在当前项目中,如果我也想采用组件化的形式进行开发,应该怎么做呢?

查看下面的代码:

当然,我们也可以将下面的代码抽取到一个js文件中,并且导出。

import Vue from 'vue'
const App={
    template:"
{{name}}
"
, data(){ return { name:"gm" } } } new Vue({ el:'#app', template:"
{{msg}}
"
, data:{ msg:'ajax' }, components:{ App } })
.vue文件封装处理

但是一个组件以一个js对象的形式进行组织和使用的时候是非常不方便的

一方面编写template模块非常的麻烦

另外一方面如果有样式的话,我们写在哪里比较合适呢?

现在,我们以一种全新的方式来组织一个vue的组件






但是,这个时候这个文件可以被正确的加载吗?

必然不可以,这种特殊的文件以及特殊的格式,必须有人帮助我们处理。

谁来处理呢?vue-loader以及vue-template-compiler。

安装vue-loader和vue-template-compiler

npm install vue-loader vue-template-compiler --save-dev

修改webpack.config.js的配置文件(在module下配置):

 {
                test: /\.vue$/,
                use: ['vue-loader']
              }

认识plugin

plugin是什么?

plugin是插件的意思,通常是用于对某个现有的架构进行扩展。

webpack中的插件,就是对webpack现有功能的各种扩展,比如打包优化,文件压缩等等。

loader和plugin区别

loader主要用于转换某些类型的模块,它是一个转换器。

plugin是插件,它是对webpack本身的扩展,是一个扩展器。

lugin的使用过程:

步骤一:通过npm安装需要使用的plugins(某些webpack已经内置的插件不需要安装)

步骤二:在webpack.config.js中的plugins中配置插件。

下面,我们就来看看可以通过哪些插件对现有的webpack打包过程进行扩容,让我们的webpack变得更加好用

添加版权的Plugin

我们先来使用一个最简单的插件,为打包的文件添加版权声明

该插件名字叫BannerPlugin,属于webpack自带的插件。

按照下面的方式来修改webpack.config.js的文件:

const path = require('path')
const webpack = require('webpack')
plugins: [
      new webpack.BannerPlugin('最终版权归aaa所有'),
  ],

重新打包程序:查看bundle.js文件的头部,看到如下信息

/*! 最终版权归aaa所有 */
打包html的plugin

目前,我们的index.html文件是存放在项目的根目录下的。

我们知道,在真实发布项目时,发布的是dist文件夹中的内容,但是dist文件夹中如果没有index.html文件,那么打包的js等文件也就没有意义了。

所以,我们需要将index.html文件打包到dist文件夹中,这个时候就可以使用HtmlWebpackPlugin插件

HtmlWebpackPlugin插件可以为我们做这些事情:

自动生成一个index.html文件(可以指定模板来生成)

将打包的js文件,自动通过script标签插入到body中

n安装HtmlWebpackPlugin插件

cnpm install html-webpack-plugin --save-dev

使用插件,修改webpack.config.js文件中plugins部分的内容如下:

这里的template表示根据什么模板来生成index.html

另外,我们需要删除之前在output中添加的publicPath属性

否则插入的script标签中的src可能会有问题

const path = require('path')
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')
    plugins: [      
      new webpack.BannerPlugin('最终版权归aaa所有'),  
      new HtmlWebpackPlugin({
        template: 'index.html'
      }),
    ],
js压缩的Plugin

在项目发布之前,我们必然需要对js等文件进行压缩处理

这里,我们就对打包的js文件进行压缩

我们使用一个第三方的插件uglifyjs-webpack-plugin,并且版本号指定1.1.1,和CLI2保持一致

cnpm install [email protected] --save-dev

修改webpack.config.js文件,使用插件:

const path = require('path')
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const UglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin')
 plugins: [
      new webpack.BannerPlugin('最终版权归aaa所有'),
      new UglifyjsWebpackPlugin()
  ], plugins: [
      new webpack.BannerPlugin('最终版权归aaa所有'),
      new HtmlWebpackPlugin({
        template: 'index.html'
      }),
      new UglifyjsWebpackPlugin()
  ],

查看打包后的bunlde.js文件,是已经被压缩过了。

搭建本地服务器

webpack提供了一个可选的本地开发服务器,这个本地服务器基于node.js搭建,内部使用express框架,可以实现我们想要的让浏览器自动刷新显示我们修改后的结果。

不过它是一个单独的模块,在webpack中使用之前需要先安装它

cnpm install --save-dev [email protected]

devserver也是作为webpack中的一个选项,选项本身可以设置如下属性:

contentBase:为哪一个文件夹提供本地服务,默认是根文件夹,我们这里要填写./dist

port:端口号

inline:页面实时刷新

historyApiFallback:在SPA页面中,依赖HTML5的history模式

webpack.config.js文件配置修改如下:

  devServer: {
      contentBase: './dist',
      inline: true
    }

我们可以再配置另外一个scripts:

   "dev":"webpack-dev-server --open"

–open参数表示直接打开浏览器

这样在控制台上输入npm run dev 就直接可以打开浏览器了

webpack.config.js文件全部内容

const path = require('path')
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const UglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin')

module.exports={
    //入口,可以是字符串,对象,数组,这是入口只有一个,所以用字符串九可以
    entry:'./src/main.js',
    //出口,通常是一个对象,里面至少有个属性path和filenama
    output:{
    path:path.resolve(__dirname,'dist'),//path是一个绝对路径
    filename:"bundle.js"
   

    },
    module:{
        rules:[
            {
                test:/\.css$/,
                use:['style-loader','css-loader'],
            },
            {
                test:/\.less@/,
                use:[{
                    loader:"style-loader"
                },
                {
                    loader:"css-loader"
                },{
                    loader:"less-loader"
                }
            ]
            },
            {
                test: /\.(png|jpg|gif|jpeg)$/,
                use: [
                  {
                    loader: 'url-loader',
                    options: {
                      // 当加载的图片, 小于limit时, 会将图片编译成base64字符串形式.
                      // 当加载的图片, 大于limit时, 需要使用file-loader模块进行加载.
                      limit: 13000,
                      name: 'img/[name].[hash:8].[ext]'
                    },
                  }
                ]
              },
              {//es6转换成es5
                test: /\.js$/,
                // exclude: 排除
                // include: 包含
                exclude: /(node_modules|bower_components)/,
                use: {
                  loader: 'babel-loader',
                  options: {
                    presets: ['es2015']
                  }
                }
              },
              {
                test: /\.vue$/,
                use: ['vue-loader']
              }
        ]
    },
    resolve: {
        // alias: 别名
        extensions: ['.js', '.css', '.vue'],
        alias: {
          'vue$': 'vue/dist/vue.esm.js'
        }
      },
      plugins: [
        new webpack.BannerPlugin('最终版权归aaa所有'),
        // new HtmlWebpackPlugin({
        //   template: 'index.html'
        // }),
        new UglifyjsWebpackPlugin()
    ],
    devServer: {
      contentBase: './dist',
      inline: true
    }
   
}

十,Vue CLI相关

如果你只是简单写几个Vue的Demo程序, 那么你不需要Vue CLI.

如果你在开发大型项目, 那么你需要, 并且必然需要使用Vue CLI

使用Vue.js开发大型应用时,我们需要考虑代码目录结构、项目结构和部署、热加载、代码单元测试等事情。

如果每个项目都要手动完成这些工作,那无以效率比较低效,所以通常我们会使用一些脚手架工具来帮助完成这些事情。

CLI是什么意思?

CLI是Command-Line Interface, 翻译为命令行界面, 但是俗称脚手架.

Vue CLI是一个官方发布 vue.js 项目脚手架

使用 vue-cli 可以快速搭建Vue开发环境以及对应的webpack配置.

Vue CLI使用前提 - Node

  • 安装NodeJS

可以直接在官方网站中下载安装.

网址: http://nodejs.cn/download/

检测安装的版本

默认情况下自动安装Node和NPM

Node环境要求8.9以上或者更高版本

  • 什么是NPM呢?

NPM的全称是Node Package Manager

是一个NodeJS包管理和分发工具,已经成为了非官方的发布Node模块(包)的标准。

后续我们会经常使用NPM来安装一些开发过程中依赖包.

  • npm安装

由于国内直接使用 npm 的官方镜像是非常慢的,这里推荐使用淘宝 NPM 镜像。

你可以使用淘宝定制的 cnpm (gzip 压缩支持) 命令行工具代替默认的 npm:

npm install -g cnpm --registry=https://registry.npm.taobao.org

这样就可以使用 cnpm 命令来安装模块了:

Vue CLI使用前提 - Webpack

Vue.js官方脚手架工具就使用了webpack模板

对所有的资源会压缩等优化操作

它在开发过程中提供了一套完整的功能,能够使得我们开发过程中变得高效。

Webpack的全局安装

npm install webpack -g

npm install [name]

Vue CLI的使用

1.安装Vue脚手架

npm install -g @vuecli

Vue CLI3初始化项目:

vue create my-project
Vue CLI2详解

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VG8fd4Nt-1641376421933)(C:\文件\web\vue\vue-md-image\image-20210604193425245.png)]

  • 目录结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u8bHQBV1-1641376421934)(C:\文件\web\vue\vue-md-image\image-20210604193711478.png)]

Runtime-Compiler和Runtime-only的区别

  • Vue程序运行过程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1QU1WDDa-1641376421935)(C:\文件\web\vue\vue-md-image\image-20210604194910507.png)]

简单总结

如果在之后的开发中,你依然使用template,就需要选择Runtime-Compiler

如果你之后的开发中,使用的是.vue文件夹开发,那么可以选择Runtime-only

  • render和template

    为什么存在这样的差异呢?

    我们需要先理解Vue应用程序是如何运行起来的。

    Vue中的模板如何最终渲染成真实DOM。

    我们来看下面的一幅图

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QcCCkkPL-1641376421936)(C:\文件\web\vue\vue-md-image\image-20210604195457518.png)]

npm run build

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HD5CVJrn-1641376421936)(C:\文件\web\vue\vue-md-image\image-20210604195521148.png)]

npm run dev

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M043H1kh-1641376421937)(C:\文件\web\vue\vue-md-image\image-20210604195545980.png)]
认识Vue CLI3
  • vue-cli 3 与 2 版本有很大区别

vue-cli 3 是基于 webpack 4 打造,vue-cli 2 还是 webapck 3

vue-cli 3 的设计原则是“0配置”,移除的配置文件根目录下的,build和config等目录

vue-cli 3 提供了 vue ui 命令,提供了可视化配置,更加人性化

移除了static文件夹,新增了public文件夹,并且index.html移动到public中

  • 创建脚手架
vue create [project name]
  • vue cli 的创建目录

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7K7NOJbe-1641376421938)(C:\文件\web\vue\vue-md-image\image-20210604195902114.png)]

  • 目录结构

  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CID6HDZG-1641376421938)(C:\文件\web\vue\vue-md-image\image-20210604200210925.png)]

  • 启动配置服务器:

    vue ui
    
  • 运行项目

  • npm run serve
    

十一 ,vue-router详解

内容概述:

认识路由

vue-router基本使用

vue-router嵌套路由

vue-router参数传递

vue-router导航守卫

keep-alive

什么是路由?

说起路由你想起了什么?

路由是一个网络工程里面的术语。

路由routing)就是通过互联的网络把信息从源地址传输到目的地址的活动. — 维基百科

在生活中, 我们有没有听说过路由的概念呢? 当然了, 路由器嘛.

路由器是做什么的? 你有想过吗?

路由器提供了两种机制: 路由和转送.

路由是决定数据包从来源目的地的路径.

转送将输入端的数据转移到合适的输出端.

路由中有一个非常重要的概念叫路由表.

路由表本质上就是一个映射表, 决定了数据包的指向

后端路由阶段

早期的网站开发整个HTML页面是由服务器来渲染的.
服务器直接生产渲染好对应的HTML页面, 返回给客户端进行展示.
但是, 一个网站, 这么多页面服务器如何处理呢?
一个页面有自己对应的网址, 也就是URL.
URL会发送到服务器, 服务器会通过正则对该URL进行匹配, 并且最后交给一个Controller进行处理.
Controller进行各种处理, 最终生成HTML或者数据, 返回给前端.
这就完成了一个IO操作.
上面的这种操作, 就是后端路由.
当我们页面中需要请求不同的路径内容时, 交给服务器来进行处理, 服务器渲染好整个页面, 并且将页面返回给客户顿.
这种情况下渲染好的页面, 不需要单独加载任何的js和css, 可以直接交给浏览器展示, 这样也有利于SEO的优化.
后端路由的缺点:
一种情况是整个页面的模块由后端人员来编写和维护的.
另一种情况是前端开发人员如果要开发页面, 需要通过PHP和Java等语言来编写页面代码.
而且通常情况下HTML代码和数据以及对应的逻辑会混在一起, 编写和维护都是非常糟糕的事情.

前端路由阶段

前后端分离阶段:
随着Ajax的出现, 有了前后端分离的开发模式.
后端只提供API来返回数据, 前端通过Ajax获取数据, 并且可以通过JavaScript将数据渲染到页面中.
这样做最大的优点就是前后端责任的清晰, 后端专注于数据上, 前端专注于交互和可视化上.
并且当移动端(iOS/Android)出现后, 后端不需要进行任何处理, 依然使用之前的一套API即可.
目前很多的网站依然采用这种模式开发.
单页面富应用阶段:
其实SPA最主要的特点就是在前后端分离的基础上加了一层前端路由.
也就是前端来维护一套路由规则.
前端路由的核心是什么呢?
改变URL,但是页面不进行整体的刷新。
如何实现呢?

URL的hash

URL的hash
URL的hash也就是锚点(#), 本质上是改变window.location的href属性.
我们可以通过直接赋值location.hash来改变href, 但是页面不发生刷新

HTML5的history模式:
1.pushState

history接口是HTML5新增的, 它有五种模式改变URL而不刷新页面.

history.pushState()

2.replaceState

history.replaceState()

3.go

history.go()

补充说明:

上面只演示了三个方法

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

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

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

认识vue-router

目前前端流行的三大框架, 都有自己的路由实现:
Angular的ngRouter
React的ReactRouter
Vue的vue-router

当然, 我们的重点是vue-router
vue-router是Vue.js官方的路由插件,它和vue.js是深度集成的,适合用于构建单页面应用。
我们可以访问其官方网站对其进行学习: https://router.vuejs.org/zh/
vue-router是基于路由和组件的
路由用于设定访问路径, 将路径和组件映射起来.
在vue-router的单页面应用中, 页面的路径的改变就是组件的切换.

安装和使用vue-router

因为我们已经学习了webpack, 后续开发中我们主要是通过工程化的方式进行开发的.
所以在后续, 我们直接使用npm来安装路由即可.
步骤一: 安装vue-router

npm install vue-router --save

步骤二: 在模块化工程中使用它(因为是一个插件, 所以可以通过Vue.use()来安装路由功能)
第一步:导入路由对象,并且调用 Vue.use(VueRouter)
第二步:创建路由实例,并且传入路由映射配置
第三步:在Vue实例中挂载创建的路由实例
使用vue-router的步骤:
第一步: 创建路由组件
第二步: 配置路由映射: 组件和路径映射关系
第三步: 使用路由: 通过和

创建router实例

在src文件夹下面创建一个router文件,并创建index.js

// 配置路由相关的信息
import VueRouter from 'vue-router'
import Vue from 'vue'


// 1.通过Vue.use(插件), 安装插件
Vue.use(VueRouter)

// 2.创建VueRouter对象
const routes = []


// 3.将router对象传入到Vue实例
export default router

挂载到Vue实例中
  • 入口文件main.js
import { createApp } from 'vue'	
import App from './App.vue'
import router from './router'

createApp(App).use(router).mount('#app')//挂载
步骤一:创建路由组件

在view文件夹下面创建Home.vue和About.vue

**步骤二:**配置组件和路径的映射关系
// 配置路由相关的信息
import VueRouter from 'vue-router'
import Vue from 'vue'

import Home from '../components/Home'
import About from '../components/About'

// 1.通过Vue.use(插件), 安装插件
Vue.use(VueRouter)

// 2.创建VueRouter对象
const routes = [
  {
    path: '',
    // redirect重定向
    redirect: '/home'
  },
  {
    path: '/home',
    component: Home
  },
  {
    path: '/about',
    component: About
  }
]
const router = new VueRouter({
  // 配置路由和组件之间的应用关系
  routes,
  mode: 'history',
  linkActiveClass: 'active'
})

// 3.将router对象传入到Vue实例
export default router


**步骤三:**使用路由

: 该标签是一个vue-router中已经内置的组件, 它会被渲染成一个标签.
: 该标签会根据当前的路径, 动态渲染出不同的组件.
网页的其他内容, 比如顶部的标题/导航, 或者底部的一些版权信息等会和处于同一个等级.
在路由切换时, 切换的是挂载的组件, 其他内容不会发生改变.

<template>
  <div id="nav">
    <router-link to="/">Homerouter-link> |
    <router-link to="/about">Aboutrouter-link>
  div>
  <router-view/>
template>
路由的默认路径

我们这里还有一个不太好的实现:
默认情况下, 进入网站的首页, 我们希望渲染首页的内容.
但是我们的实现中, 默认没有显示首页组件, 必须让用户点击才可以.
如何可以让路径默认跳到到首页, 并且渲染首页组件呢?
非常简单, 我们只需要配置多配置一个映射就可以了.

// 2.创建VueRouter对象
const routes = [
  {
    path: '',
    // redirect重定向
    redirect: '/home'
  },
  {
    path: '/home',
    component: Home
  },
  {
    path: '/about',
    component: About
  }
]

配置解析:
我们在routes中又配置了一个映射.
path配置的是根路径: /
redirect是重定向, 也就是我们将根路径重定向到/home的路径下, 这样就可以得到我们想要的结果了.

HTML5的History模式

我们前面说过改变路径的方式有两种:
URL的hash
HTML5的history
默认情况下, 路径的改变使用的URL的hash.
如果希望使用HTML5的history模式, 非常简单, 进行如下配置即可

const router = new VueRouter({
  // 配置路由和组件之间的应用关系
  routes,
  mode: 'history',
})
router-link补充

在前面的中, 我们只是使用了一个属性: to, 用于指定跳转的路径.
还有一些其他属性:
tag: tag可以指定之后渲染成什么组件, 比如上面的代码会被渲染成一个

  • 元素, 而不是
    replace: replace不会留下history记录, 所以指定replace的情况下, 后退键返回不能返回到上一个页面中
    active-class: 当对应的路由匹配成功时, 会自动给当前元素设置一个router-link-active的class, 设置active-class可以修改默认的名称.
    在进行高亮显示的导航菜单或者底部tabbar时, 会使用到该类.
    但是通常不会修改类的属性, 会直接使用默认的router-link-active即可.
  • 修改linkActiveClass

    该class具体的名称也可以通过router实例的属性进行修改

    const router = new VueRouter({
      // 配置路由和组件之间的应用关系
      routes,
      mode: 'history',
      linkActiveClass: 'active'
    })
    

    exact-active-class

    类似于active-class, 只是在精准匹配下才会出现的class.

    后面看到嵌套路由时, 我们再看下这个属性.

    路由代码跳转

    有时候, 页面的跳转可能需要执行对应的JavaScript代码, 这个时候, 就可以使用第二种跳转方式了

    比如, 我们将代码修改如下:

    <template>
      <div id="app">
        <h2>我是APP组件h2>
        
        
        
        
        <button @click="homeClick">首页button>
        <button @click="aboutClick">关于button>
        <router-view>router-view>
      div>
    template>
    
    <script>
    export default {
      name: 'App',
      methods: {
        homeClick() {
          // 通过代码的方式修改路由 vue-router
          // push => pushState
          // this.$router.push('/home')
          this.$router.replace('/home')
          console.log('homeClick');
        },
        aboutClick() {
          // this.$router.push('/about')
          this.$router.replace('/about')
          console.log('aboutClick');
        }
      }
    }
    script>
    
    <style>
      /*.router-link-active {*/
        /*color: #f00;*/
      /*}*/
    
      .active {
        color: #f00;
      }
    style>
    
    
    动态路由

    在某些情况下,一个页面的path路径可能是不确定的,比如我们进入用户界面时,希望是如下的路径:
    /user/aaaa或/user/bbbb
    除了有前面的/user之外,后面还跟上了用户的ID
    这种path和Component的匹配关系,我们称之为动态路由(也是路由传递数据的一种方式)。

     <router-link to="/">Homerouter-link> |
      <router-link to="/about/123">Aboutrouter-link>
    
     {
        path: '/about/:id',
        name: 'About',
      }
    

    在当前页面可以用route.params.id来获取参数id

    <template>
      <div class="about">
         <div>
        <h2>{{$route.params.id}}h2>
      div>
      div>
    template>
    
    
    认识路由的懒加载

    官方给出了解释:
    当打包构建应用时,Javascript 包会变得非常大,影响页面加载。
    如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了
    官方在说什么呢?
    首先, 我们知道路由中通常会定义很多不同的页面.
    这个页面最后被打包在哪里呢? 一般情况下, 是放在一个js文件中.
    但是, 页面这么多放在一个js文件中, 必然会造成这个页面非常的大.
    如果我们一次性从服务器请求下来这个页面, 可能需要花费一定的时间, 甚至用户的电脑上还出现了短暂空白的情况.
    如何避免这种情况呢? 使用路由懒加载就可以了.
    路由懒加载做了什么?
    路由懒加载的主要作用就是将路由对应的组件打包成一个个的js代码块.
    只有在这个路由被访问到的时候, 才加载对应的组件

    懒加载的方式

    方式一: 结合Vue的异步组件和Webpack的代码分析

    const Home = resolve => { require.ensure(['../components/Home.vue'], () => { resolve(require('../components/Home.vue')) })};
    

    方式二: AMD写法

    const About = resolve => require(['../components/About.vue'], resolve);
    

    方式三: 在ES6中, 我们可以有更加简单的写法来组织Vue异步组件和Webpack的代码分割.

    const Home = () => import('../components/Home.vue')
    

    cli3中默认方式

    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
    
    认识嵌套路由

    嵌套路由是一个很常见的功能
    比如在home页面中, 我们希望通过/home/news和/home/message访问一些内容.
    一个路径映射一个组件, 访问这两个路径也会分别渲染两个组件.
    路径和组件的关系如下:

    路径                        组件
    /home  ----->              home
    /home/new  ----->		   new
    /home/message  ----->      message
    /about  ----->             about 
    

    实现嵌套路由有两个步骤:
    创建对应的子组件, 并且在路由映射中配置对应的子路由.
    在组件内部使用标签.

    嵌套路由实现

    在页面中使用

        <router-link to='/home/message'>消息router-link>|
        <router-link to='/home/new'>新闻router-link>
    	<router-view>router-view>
    

    router—>index.js定义子组件

    嵌套默认的子组件路径

    import { createRouter, createWebHistory } from 'vue-router'
    const Home =() => import("../components/Home")
    const Message = () =>import('../components/Message')
    const New = () =>import('../components/New')
    const About = () =>import('../components/About')
    // console.log(Home);
    const routes = [
      {
        path: '',//默认路由
        redirect: '/home'
      },
      {
        path: '/home',
        component: Home,
        children:[//嵌套子组件
          {
            path: 'message',
            component:Message
            
          },
          {
            path: 'new',
            component:New
            
          }
        ]
    
      },
      {
        path: '/about/:id',
        name: 'About',
        component:About
      },
      
    ]
    
    export default router
    
    
    组件间传递参数
    • 准备工作

      为了演示传递参数, 我们这里再创建一个组件, 并且将其配置好
      第一步: 创建新的组件Profile.vue
      第二步: 配置路由映射
      第三步: 添加跳转的

    • 传递参数的方式

      传递参数主要有两种类型: params和query

      params的类型:

      配置路由格式: /router/:id
      传递的方式: 在path后面跟上对应的值
      传递后形成的路径: /router/123, /router/abc

      query的类型:

      配置路由格式: /router, 也就是普通配置
      传递的方式: 对象中使用query的key作为传递方式
      传递后形成的路径: /router?id=123, /router?id=abc

      如何使用它们呢? 也有两种方式: 的方式和JavaScript代码方式

    传递参数方式一:
    <router-link to="{
             path:'/profile/'+123,
             query:{name:'why',age:'18'}
                }">档案router-link>
    
    传递参数方式二: JavaScript代码
    <script>
      export default{
        name:"App",
        methods:{
          toProfile(){
            this.$router.push({
              path:"/profile/ + 123",
              query:{name:'why',age:18}
            })
          }
        }
      }
    
    script>
    
    • 获取参数

      获取参数通过 r o u t e 对 象 获 取 的 . 在 使 用 了 v u e − r o u t e r 的 应 用 中 , 路 由 对 象 会 被 注 入 每 个 组 件 中 , 赋 值 为 t h i s . route对象获取的. 在使用了 vue-router 的应用中,路由对象会被注入每个组件中,赋值为 this. route.使vuerouterthis.route ,并且当路由切换时,路由对象会被更新。
      通过$route获取传递的信息如下:

      <template>
        <div>
            profile
            <p>{{$route.params}}p>
            <p>{{$route.query}}p>
        div>
      template>
      
      <script>
      export default {
          name:"Profile"
      }
      script>
      
      
    r o u t e 和 route和 routerouter是有区别的

    r o u t e 和 route和 routerouter是有区别的
    1. r o u t e r 为 V u e R o u t e r 实 例 , 想 要 导 航 到 不 同 U R L , 则 使 用 router为VueRouter实例,想要导航到不同URL,则使用 routerVueRouterURL使router.push方法
    2.$route为当前router跳转对象里面可以获取name、path、query、params等

    导航守卫
    为什么使用导航守卫?

    我们来考虑一个需求: 在一个SPA应用中, 如何改变网页的标题呢?
    网页标题是通过来显示的, 但是SPA只有一个固定的HTML, 切换不同的页面时, 标题并不会改变.
    但是我们可以通过JavaScript来修改的内容.window.document.title = ‘新的标题’.<br/> 那么在Vue项目中, 在哪里修改? 什么时候修改比较合适呢?<br/> 普通的修改方式:<br/> 我们比较容易想到的修改标题的位置是每一个路由对应的组件.vue文件中.<br/> 通过mounted声明周期函数, 执行对应的代码进行修改即可.<br/> 但是当页面比较多时, 这种方式不容易维护(因为需要在多个页面执行类似的代码).<br/> 有没有更好的办法呢? 使用导航守卫即可.<br/> 什么是导航守卫?<br/> vue-router提供的导航守卫主要用来监听监听路由的进入和离开的.<br/> vue-router提供了beforeEach和afterEach的钩子函数, 它们会在路由即将改变前和改变后触发.</p> <h6>导航守卫使用</h6> <p>我们可以利用beforeEach来完成标题的修改.</p> <p>首先, 我们可以在钩子当中定义一些标题, 可以利用meta来定义</p> <p>其次, 利用导航守卫,修改我们的标题.</p> <p>导航钩子的三个参数解析:</p> <ol> <li>to: 即将要进入的目标的路由对象.</li> <li>from: 当前导航即将要离开的路由对象.</li> <li>next: 调用该方法后, 才能进入下一个钩子</li> </ol> <pre><code class="prism language-js"><span class="token keyword">import</span> <span class="token punctuation">{</span> createRouter<span class="token punctuation">,</span> createWebHistory <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'vue-router'</span> <span class="token keyword">const</span> <span class="token function-variable function">Home</span> <span class="token operator">=</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token keyword">import</span><span class="token punctuation">(</span><span class="token string">"../components/Home"</span><span class="token punctuation">)</span> <span class="token comment">// console.log(Home);</span> <span class="token keyword">const</span> routes <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token punctuation">{</span> path<span class="token operator">:</span> <span class="token string">''</span><span class="token punctuation">,</span><span class="token comment">//默认路由</span> redirect<span class="token operator">:</span> <span class="token string">'/home'</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> path<span class="token operator">:</span> <span class="token string">'/home'</span><span class="token punctuation">,</span> component<span class="token operator">:</span> Home<span class="token punctuation">,</span> meta<span class="token operator">:</span><span class="token punctuation">{</span> title<span class="token operator">:</span><span class="token string">"首页"</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">]</span> <span class="token keyword">const</span> router <span class="token operator">=</span> <span class="token function">createRouter</span><span class="token punctuation">(</span><span class="token punctuation">{</span> history<span class="token operator">:</span> <span class="token function">createWebHistory</span><span class="token punctuation">(</span>process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">BASE_URL</span><span class="token punctuation">)</span><span class="token punctuation">,</span> routes <span class="token punctuation">}</span><span class="token punctuation">)</span> router<span class="token punctuation">.</span><span class="token function">beforeEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">to<span class="token punctuation">,</span><span class="token keyword">from</span><span class="token punctuation">,</span>next</span><span class="token punctuation">)</span> <span class="token operator">=></span><span class="token punctuation">{</span> window<span class="token punctuation">.</span>document<span class="token punctuation">.</span>title<span class="token operator">=</span>to<span class="token punctuation">.</span>meta<span class="token punctuation">.</span>title <span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token keyword">export</span> <span class="token keyword">default</span> router </code></pre> <h6>导航守卫补充</h6> <p>补充一:如果是后置钩子, 也就是afterEach, 不需要主动调用next()函数.<br> 补充二: 上面我们使用的导航守卫, 被称之为全局守卫.<br> 路由独享的守卫.<br> 组件内的守卫.</p> <p>更多内容, 可以查看官网进行学习:<br> https://router.vuejs.org/zh/guide/advanced/navigation-guards.html#%E8%B7%AF%E7%94%B1%E7%8B%AC%E4%BA%AB%E7%9A%84%E5%AE%88%E5%8D%AB</p> <h5>keep-alive遇见vue-router</h5> <p>keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染。<br> 它们有两个非常重要的属性:<br> include - 字符串或正则表达,只有匹配的组件会被缓存<br> exclude - 字符串或正则表达式,任何匹配的组件都不会被缓存<br> router-view 也是一个组件,如果直接被包在 keep-alive 里面,所有路径匹配到的视图组件都会被缓存:</p> <p>cli2写法:</p> <pre><code class="prism language-html"> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>keep-alive</span><span class="token punctuation">></span></span> <span class="token comment"><!-- 所有路径匹配的视图组件都会被缓存 --></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>router-view</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>router-view</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>keep-alive</span><span class="token punctuation">></span></span> </code></pre> <p>cli3最新写法:</p> <pre><code class="prism language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>router-view</span> <span class="token attr-name">v-slot</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>{ Component }<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>keep-alive</span> <span class="token attr-name">exclude</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Detail<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token comment"><!--Detail不被缓存--></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>component</span> <span class="token attr-name">:is</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Component<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>keep-alive</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>router-view</span><span class="token punctuation">></span></span> </code></pre> <p>通过create声明周期函数来验证</p> <ul> <li>缓存的结果是create只被创建一次</li> </ul> <h5>TabBar实现思路</h5> <p>创建底部导航和页面实例</p> <p>1.如果在下方有一个单独的TabBar组件,你如何封装自定义TabBar组件,在APP中使用<br> 让TabBar出于底部,并且设置相关的样式<br> 2.TabBar中显示的内容由外界决定<br> 定义插槽<br> flex布局平分TabBar<br> 3.自定义TabBarItem,可以传入 图片和文字<br> 定义TabBarItem,并且定义两个插槽:图片、文字。<br> 给两个插槽外层包装div,用于设置样式。<br> 填充插槽,实现底部TabBar的效果<br> 4.传入 高亮图片<br> 定义另外一个插槽,插入active-icon的数据<br> 定义一个变量isActive,通过v-show来决定是否显示对应的icon<br> 5.TabBarItem绑定路由数据<br> 安装路由:npm install vue-router —save<br> 完成router/index.js的内容,以及创建对应的组件<br> main.js中注册router<br> APP中加入组件<br> 6.点击item跳转到对应路由,并且动态决定isActive<br> 监听item的点击,通过this.<span class="katex--inline"><span class="katex"><span class="katex-mathml"> r o u t e r . r e p l a c e ( ) 替 换 路 由 路 径 通 过 t h i s . router.replace()替换路由路径 通过this. </span><span class="katex-html"><span class="base"><span class="strut" style="height: 1em; vertical-align: -0.25em;"></span><span class="mord mathdefault" style="margin-right: 0.02778em;">r</span><span class="mord mathdefault">o</span><span class="mord mathdefault">u</span><span class="mord mathdefault">t</span><span class="mord mathdefault">e</span><span class="mord mathdefault" style="margin-right: 0.02778em;">r</span><span class="mord">.</span><span class="mord mathdefault" style="margin-right: 0.02778em;">r</span><span class="mord mathdefault">e</span><span class="mord mathdefault">p</span><span class="mord mathdefault" style="margin-right: 0.01968em;">l</span><span class="mord mathdefault">a</span><span class="mord mathdefault">c</span><span class="mord mathdefault">e</span><span class="mopen">(</span><span class="mclose">)</span><span class="mord cjk_fallback">替</span><span class="mord cjk_fallback">换</span><span class="mord cjk_fallback">路</span><span class="mord cjk_fallback">由</span><span class="mord cjk_fallback">路</span><span class="mord cjk_fallback">径</span><span class="mord cjk_fallback">通</span><span class="mord cjk_fallback">过</span><span class="mord mathdefault">t</span><span class="mord mathdefault">h</span><span class="mord mathdefault">i</span><span class="mord mathdefault">s</span><span class="mord">.</span></span></span></span></span>route.path.indexOf(this.link) !== -1来判断是否是active<br> 7.动态计算active样式<br> 封装新的计算属性:this.isActive ? {‘color’: ‘brown’} : {}</p> <p>MainTabBar.vue</p> <pre><code class="prism language-vue"><template> <div> <tab-bar> <tab-bar-item path="/home" activeColor='deepPink'> <template v-slot:item-icon> <img src="@/assets/img/tabbar/home.svg" alt=""> </template> <template v-slot:item-icon-active> <img src="@/assets/img/tabbar/home_active.svg" alt=""> </template> <template v-slot:item-text> <div> <span>首页</span> </div> </template> </tab-bar-item> <tab-bar-item path="/category" activeColor='deepPink'> <template v-slot:item-icon> <img src="@/assets/img/tabbar/category.svg" alt=""> </template> <template v-slot:item-icon-active> <img src="@/assets/img/tabbar/category_active.svg" alt=""> </template> <template v-slot:item-text> <span>分类</span> </template> </tab-bar-item> <tab-bar-item path="/shopcart" activeColor='deepPink'> <template v-slot:item-icon> <img src="@/assets/img/tabbar/shopcart.svg" alt=""> </template> <template v-slot:item-icon-active> <img src="@/assets/img/tabbar/shopcart_active.svg" alt=""> </template> <template v-slot:item-text> <span>购物车</span> </template> </tab-bar-item> <tab-bar-item path="/profile" activeColor='deepPink'> <template v-slot:item-icon> <img src="@/assets/img/tabbar/profile.svg" alt=""> </template> <template v-slot:item-icon-active> <img src="@/assets/img/tabbar/profile_active.svg" alt=""> </template> <template v-slot:item-text> <span>我的</span> </template> </tab-bar-item> </tab-bar> </div> </template> <script> import TabBar from '@/components/common/tabbar/TabBar.vue' import TabBarItem from '@/components/common/tabbar/TabBarItem.vue' export default { name: 'MainTabBar', components: { TabBar, TabBarItem } } </script> <style> .tab-bar-item img { width: 24px; height: 24px; margin-top: 3px; vertical-align: middle; margin-bottom: 2px; } .tab-bar-item span{ display: block; } </style> </code></pre> <p>TabBar.vue</p> <pre><code class="prism language-vue"><template> <div id="tab-bar"> <slot></slot> </div> </template> <script> export default { name: "TabBar" } </script> <style scoped> #tab-bar { display: flex; background-color: #f6f6f6; position: fixed; left: 0; right: 0; bottom: 0; box-shadow: 0 -1px 1px rgba(100,100,100,.2); z-index: 1; } </style> </code></pre> <p>TabBarItem.vue</p> <pre><code class="prism language-vue"><template> <div class="tab-bar-item"> <!-- <slot></slot> --> <div @click="itemClick"> <div v-if="!isActive"> <slot name="item-icon"></slot> </div> <div v-else > <slot name="item-icon-active"></slot> </div> </div> <div :style='activeStyle'> <slot name="item-text"></slot> </div> </div> </template> <script> export default { name:"TabBarItem", props:{ path:String, activeColor:{ type:String, defaule:"red" } }, data(){ return{ // isActive:true } }, methods:{ itemClick(){ this.$router.replace(this.path) } }, computed:{ isActive(){ return this.$route.path.indexOf(this.path) !==-1 }, activeStyle() { return this.isActive ? {color:this.activeColor} : {} // return console.log(this.isActive) } } } </script> <style scoped> .tab-bar-item { flex: 1; text-align: center; height: 49px; font-size: 14px; } .tab-bar-item .active{ color: brown; } </style> </code></pre> <h4>十二,Vuex详解</h4> <h6>Vuex是做什么的?</h6> <p>官方解释:Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。<br> 它采用 集中式存储管理 应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。<br> Vuex 也集成到 Vue 的官方调试工具 devtools extension,提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调试功能。<br> 状态管理到底是什么?<br> 状态管理模式、集中式存储管理这些名词听起来就非常高大上,让人捉摸不透。<br> 其实,你可以简单的将其看成把需要多个组件共享的变量全部存储在一个对象里面。<br> 然后,将这个对象放在顶层的Vue实例中,让其他组件可以使用。<br> 那么,多个组件是不是就可以共享这个对象中的所有变量属性了呢?<br> 等等,如果是这样的话,为什么官方还要专门出一个插件Vuex呢?难道我们不能自己封装一个对象来管理吗?<br> 当然可以,只是我们要先想想VueJS带给我们最大的便利是什么呢?没错,就是响应式。<br> 如果你自己封装实现一个对象能不能保证它里面所有的属性做到响应式呢?当然也可以,只是自己封装可能稍微麻烦一些。<br> 不用怀疑,Vuex就是为了提供这样一个在多个组件间共享状态的插件,用它就可以了。</p> <h6>管理什么状态呢?</h6> <p>但是,有什么状态时需要我们在多个组件间共享的呢?<br> 如果你做过大型开放,你一定遇到过多个状态,在多个界面间的共享问题。<br> 比如用户的登录状态、用户名称、头像、地理位置信息等等。<br> 比如商品的收藏、购物车中的物品等等。<br> 这些状态信息,我们都可以放在统一的地方,对它进行保存和管理,而且它们还是响应式的(待会儿我们就可以看到代码了,莫着急)。</p> <p>OK,从理论上理解了状态管理之后,让我们从实际的代码再来看看状态管理。<br> 毕竟,Talk is cheap, Show me the code.(来自Linus)</p> <p>我们先来看看但界面的状态管理吧.</p> <h6>单界面的状态管理</h6> <p>我们知道,要在单个组件中进行状态管理是一件非常简单的事情<br> 什么意思呢?我们来看下面的图片。</p> <p>​ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wIcO2JSj-1641376421939)(C:\文件\web\vue\vue-md-image\单页面状态管理.png)]</p> <p>这图片中的三种东西,怎么理解呢?<br> State:不用多说,就是我们的状态。(你姑且可以当做就是data中的属性)<br> View:视图层,可以针对State的变化,显示不同的信息。(这个好理解吧?)<br> Actions:这里的Actions主要是用户的各种操作:点击、输入等等,会导致状态的改变。</p> <p>写点代码,加深理解:<br> 看下右边的代码效果, 肯定会实现吧?</p> <pre><code class="prism language-vue"><template> <div> <h2>当前计数:{{counter}}</h2> <button @click="counter++">+</button> <button @click="counter--">-</button> </div> </template> <script> export default { name:"Axios1", data(){ return{ counter:0 } } } </script> </code></pre> <p>在这个案例中,我们有木有状态需要管理呢?没错,就是个数counter。</p> <p>counter需要某种方式被记录下来,也就是我们的State。</p> <p>counter目前的值需要被显示在界面中,也就是我们的View部分。</p> <p>界面发生某些操作时(我们这里是用户的点击,也可以是用户的input),需要去更新状态,也就是我们的Actions</p> <p>这不就是上面的流程图了吗?</p> <h6>多界面状态管理</h6> <p>Vue已经帮我们做好了单个界面的状态管理,但是如果是多个界面呢?<br> 多个试图都依赖同一个状态(一个状态改了,多个界面需要进行更新)<br> 不同界面的Actions都想修改同一个状态(Home.vue需要修改,Profile.vue也需要修改这个状态)<br> 也就是说对于某些状态(状态1/状态2/状态3)来说只属于我们某一个试图,但是也有一些状态(状态a/状态b/状态c)属于多个试图共同想要维护的<br> 状态1/状态2/状态3你放在自己的房间中,你自己管理自己用,没问题。<br> 但是状态a/状态b/状态c我们希望交给一个大管家来统一帮助我们管理!!!<br> 没错,Vuex就是为我们提供这个大管家的工具。<br> 全局单例模式(大管家)<br> 我们现在要做的就是将共享的状态抽取出来,交给我们的大管家,统一进行管理。<br> 之后,你们每个试图,按照我规定好的规定,进行访问和修改等操作。<br> 这就是Vuex背后的基本思想。</p> <h6>Vuex状态管理图例</h6> <p>[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rOAQ2pV3-1641376421940)(C:\文件\web\vue\vue-md-image\xuex状态管理.png)]</p> <h6>简单的案例</h6> <p>用vuex实现前面的案例</p> <p>1.安装vuex3.0</p> <pre><code>npm install --save vuex </code></pre> <p>安装vuex4.0</p> <pre><code>npm install vuex@next --save </code></pre> <p>首先,我们需要在某个地方存放我们的Vuex代码:</p> <p>这里,我们先在src下创建一个文件夹store,并且在其中创建一个index.js文件</p> <p>2.在index.js文件中写入如下代码</p> <p>vuex3.0</p> <pre><code class="prism language-js"><span class="token keyword">import</span> Vue <span class="token keyword">from</span> <span class="token string">'vue'</span> <span class="token keyword">import</span> Vuex <span class="token keyword">from</span> <span class="token string">'vuex'</span> Vue<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span>Vuex<span class="token punctuation">)</span> <span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">new</span> <span class="token class-name">Vuex<span class="token punctuation">.</span>Store</span><span class="token punctuation">(</span><span class="token punctuation">{</span> state<span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token comment">//同步操作,尽量别在里面进行异步操作</span> mutations<span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token comment">//异步操作</span> actions<span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token comment">//context上下文</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> modules<span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> getters<span class="token operator">:</span><span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> </code></pre> <p>vuex4.0</p> <pre><code class="prism language-js"><span class="token keyword">import</span> <span class="token punctuation">{</span> createStore <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'vuex'</span> <span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token function">createStore</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token comment">//状态管理</span> state<span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token comment">//同步操作</span> mutations<span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token comment">//异步操作</span> actions<span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token comment">//模块开发,可以创建管理多个store</span> modules<span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> </code></pre> <p>3.挂载到Vue实例中</p> <pre><code class="prism language-js"><span class="token keyword">import</span> <span class="token punctuation">{</span> createApp <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'vue'</span> <span class="token keyword">import</span> App <span class="token keyword">from</span> <span class="token string">'./App.vue'</span> <span class="token keyword">import</span> router <span class="token keyword">from</span> <span class="token string">'./router'</span> <span class="token keyword">import</span> store <span class="token keyword">from</span> <span class="token string">'./store'</span> <span class="token function">createApp</span><span class="token punctuation">(</span>App<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span>store<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span>router<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">mount</span><span class="token punctuation">(</span><span class="token string">'#app'</span><span class="token punctuation">)</span> </code></pre> <p>4.使用Vuex的counter</p> <p>好的,这就是使用Vuex最简单的方式了。<br> 我们来对使用步骤,做一个简单的小节:<br> 1.提取出一个公共的store对象,用于保存在多个组件中共享的状态<br> 2.将store对象放置在new Vue对象中,这样可以保证在所有的组件中都可以使用到<br> 3.在其他组件中使用store对象中保存的状态即可<br> 通过this.<span class="katex--inline"><span class="katex"><span class="katex-mathml"> s t o r e . s t a t e . 属 性 的 方 式 来 访 问 状 态 通 过 t h i s . store.state.属性的方式来访问状态 通过this. </span><span class="katex-html"><span class="base"><span class="strut" style="height: 0.69444em; vertical-align: 0em;"></span><span class="mord mathdefault">s</span><span class="mord mathdefault">t</span><span class="mord mathdefault">o</span><span class="mord mathdefault" style="margin-right: 0.02778em;">r</span><span class="mord mathdefault">e</span><span class="mord">.</span><span class="mord mathdefault">s</span><span class="mord mathdefault">t</span><span class="mord mathdefault">a</span><span class="mord mathdefault">t</span><span class="mord mathdefault">e</span><span class="mord">.</span><span class="mord cjk_fallback">属</span><span class="mord cjk_fallback">性</span><span class="mord cjk_fallback">的</span><span class="mord cjk_fallback">方</span><span class="mord cjk_fallback">式</span><span class="mord cjk_fallback">来</span><span class="mord cjk_fallback">访</span><span class="mord cjk_fallback">问</span><span class="mord cjk_fallback">状</span><span class="mord cjk_fallback">态</span><span class="mord cjk_fallback">通</span><span class="mord cjk_fallback">过</span><span class="mord mathdefault">t</span><span class="mord mathdefault">h</span><span class="mord mathdefault">i</span><span class="mord mathdefault">s</span><span class="mord">.</span></span></span></span></span>store.commit(‘mutation中方法’)来修改状态<br> 注意事项:<br> 我们通过提交mutation的方式,而非直接改变store.state.count。<br> 这是因为Vuex可以更明确的追踪状态的变化,所以不要直接改变store.state.count的值</p> <pre><code class="prism language-vue"><template> <div> <h2>{{counter}}</h2> <button @click="increment"> + </button> <button @click="decrement"> - </button> </div> </template> <script> export default { name:"vuex1.vue", data(){ return{ } }, computed:{ counter(){ return this.$store.state.count } }, methods:{ increment(){ this.$store.commit('increment') }, decrement(){ this.$store.commit('decrement') } } } </script> </code></pre> <p>5.store–index.js完成对状态的操作</p> <pre><code class="prism language-js"><span class="token keyword">import</span> <span class="token punctuation">{</span> createStore <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'vuex'</span> <span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token function">createStore</span><span class="token punctuation">(</span><span class="token punctuation">{</span> state<span class="token operator">:</span> <span class="token punctuation">{</span> count<span class="token operator">:</span><span class="token number">0</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> mutations<span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token function">increment</span><span class="token punctuation">(</span><span class="token parameter">state</span><span class="token punctuation">)</span><span class="token punctuation">{</span> state<span class="token punctuation">.</span>count<span class="token operator">++</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token function">decrement</span><span class="token punctuation">(</span><span class="token parameter">state</span><span class="token punctuation">)</span><span class="token punctuation">{</span> state<span class="token punctuation">.</span>count<span class="token operator">--</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> actions<span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> modules<span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> </code></pre> <h5>Vuex核心概念</h5> <p>Vuex有几个比较核心的概念:<br> State<br> Getters<br> Mutation<br> Action<br> Module<br> 我们对它进行一一介绍.</p> <h6>State单一状态树</h6> <p>Vuex提出使用单一状态树, 什么是单一状态树呢?<br> 英文名称是Single Source of Truth,也可以翻译成单一数据源。<br> 但是,它是什么呢?我们来看一个生活中的例子。<br> OK,我用一个生活中的例子做一个简单的类比。<br> 我们知道,在国内我们有很多的信息需要被记录,比如上学时的个人档案,工作后的社保记录,公积金记录,结婚后的婚姻信息,以及其他相关的户口、医疗、文凭、房产记录等等(还有很多信息)。<br> 这些信息被分散在很多地方进行管理,有一天你需要办某个业务时(比如入户某个城市),你会发现你需要到各个对应的工作地点去打印、盖章各种资料信息,最后到一个地方提交证明你的信息无误。<br> 这种保存信息的方案,不仅仅低效,而且不方便管理,以及日后的维护也是一个庞大的工作(需要大量的各个部门的人力来维护,当然国家目前已经在完善我们的这个系统了)。<br> 这个和我们在应用开发中比较类似:<br> 如果你的状态信息是保存到多个Store对象中的,那么之后的管理和维护等等都会变得特别困难。<br> 所以Vuex也使用了单一状态树来管理应用层级的全部状态。<br> 单一状态树能够让我们最直接的方式找到某个状态的片段,而且在之后的维护和调试过程中,也可以非常方便的管理和维护。</p> <h6>Getters基本使用</h6> <p>有时候,我们需要从store中获取一些state变异后的状态,比如下面的Store中:</p> <p>获取学生年龄大于20的个数。</p> <p>可以在组件中改变,但是官方不提倡</p> <pre><code class="prism language-js"> computed<span class="token operator">:</span><span class="token punctuation">{</span> <span class="token function">getOldAge1</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">.</span>$store<span class="token punctuation">.</span>state<span class="token punctuation">.</span>students<span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span> <span class="token parameter">s</span> <span class="token operator">=></span> s<span class="token punctuation">.</span>age<span class="token operator">>=</span><span class="token number">20</span><span class="token punctuation">)</span><span class="token punctuation">.</span>length <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> </code></pre> <p>我们可以在Store中定义getters</p> <pre><code class="prism language-js"> <span class="token function">getOldAge</span><span class="token punctuation">(</span><span class="token parameter">state</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">return</span> state<span class="token punctuation">.</span>students<span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token parameter">s</span><span class="token operator">=></span>s<span class="token punctuation">.</span>age<span class="token operator">>=</span><span class="token number">20</span><span class="token punctuation">)</span><span class="token punctuation">.</span>length <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> </code></pre> <h6>Getters作为参数和传递参数</h6> <p>getters默认是不能传递参数的, 如果希望传递参数, 那么只能让getters本身返回另一个函数.</p> <p>比如上面的案例中,我们希望根据age获取用户的信息</p> <pre><code class="prism language-js"> <span class="token function">moreAge</span><span class="token punctuation">(</span><span class="token parameter">state</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">age</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">return</span> state<span class="token punctuation">.</span>students<span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token parameter">s</span> <span class="token operator">=></span> s<span class="token punctuation">.</span>age <span class="token operator">></span> age<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> </code></pre> <h6>Mutation状态更新</h6> <p>Vuex的store状态的更新唯一方式:提交Mutation<br> Mutation主要包括两部分:<br> 字符串的事件类型(type)<br> 一个回调函数(handler),该回调函数的第一个参数就是state。<br> mutation的定义方式:</p> <pre><code class="prism language-js"> mutations<span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token function">increment</span><span class="token punctuation">(</span><span class="token parameter">state</span><span class="token punctuation">)</span><span class="token punctuation">{</span> state<span class="token punctuation">.</span>count<span class="token operator">++</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token function">decrement</span><span class="token punctuation">(</span><span class="token parameter">state</span><span class="token punctuation">)</span><span class="token punctuation">{</span> state<span class="token punctuation">.</span>count<span class="token operator">--</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> </code></pre> <p>通过mutation更新,通过组件提交过来</p> <pre><code class="prism language-js"> methods<span class="token operator">:</span><span class="token punctuation">{</span> <span class="token function">increment</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>$store<span class="token punctuation">.</span><span class="token function">commit</span><span class="token punctuation">(</span><span class="token string">'increment'</span><span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token function">decrement</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>$store<span class="token punctuation">.</span><span class="token function">commit</span><span class="token punctuation">(</span><span class="token string">'decrement'</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> </code></pre> <h6>Mutation传递参数</h6> <p>在通过mutation更新数据的时候, 有可能我们希望携带一些额外的参数<br> 参数被称为是mutation的载荷(Payload)<br> Mutation中的代码:</p> <p>store</p> <pre><code class="prism language-js"> <span class="token function">decrement1</span><span class="token punctuation">(</span><span class="token parameter">state<span class="token punctuation">,</span>n</span><span class="token punctuation">)</span><span class="token punctuation">{</span> state<span class="token punctuation">.</span>count <span class="token operator">-=</span> n<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> </code></pre> <p>组件中methods中</p> <pre><code class="prism language-js"> <span class="token function">decrement1</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>$store<span class="token punctuation">.</span><span class="token function">commit</span><span class="token punctuation">(</span><span class="token string">'decrement1'</span><span class="token punctuation">,</span><span class="token number">2</span><span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> </code></pre> <p>但是如果参数不是一个呢?<br> 比如我们有很多参数需要传递.<br> 这个时候, 我们通常会以对象的形式传递, 也就是payload是一个对象.<br> 这个时候可以再从对象中取出相关的信息.</p> <p>store mutation</p> <pre><code class="prism language-js"> <span class="token function">changeCount</span><span class="token punctuation">(</span><span class="token parameter">state<span class="token punctuation">,</span>payload</span><span class="token punctuation">)</span><span class="token punctuation">{</span> state<span class="token punctuation">.</span>count <span class="token operator">=</span> payload<span class="token punctuation">.</span>count <span class="token punctuation">}</span> </code></pre> <p>组件中methods中</p> <pre><code class="prism language-js"> <span class="token function">changeCount</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>$store<span class="token punctuation">.</span><span class="token function">commit</span><span class="token punctuation">(</span><span class="token string">'changeCount'</span><span class="token punctuation">,</span><span class="token punctuation">{</span>count<span class="token operator">:</span><span class="token number">5</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> </code></pre> <h6>Mutation提交风格</h6> <p>上面的通过commit进行提交是一种普通的方式<br> Vue还提供了另外一种风格, 它是一个包含type属性的对象</p> <pre><code class="prism language-js"> <span class="token function">changeCount1</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>$store<span class="token punctuation">.</span><span class="token function">commit</span><span class="token punctuation">(</span><span class="token punctuation">{</span> type<span class="token operator">:</span><span class="token string">'changeCount1'</span><span class="token punctuation">,</span> count<span class="token operator">:</span><span class="token number">100</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> </code></pre> <p>Mutation中的处理方式是将整个commit的对象作为payload使用, 所以代码没有改变, 依然如下</p> <pre><code class="prism language-js"> <span class="token function">changeCount1</span><span class="token punctuation">(</span><span class="token parameter">state<span class="token punctuation">,</span>payload</span><span class="token punctuation">)</span><span class="token punctuation">{</span> state<span class="token punctuation">.</span>count <span class="token operator">=</span> payload<span class="token punctuation">.</span>count <span class="token punctuation">}</span> </code></pre> <h6>Mutation响应规则</h6> <p>Vuex的store中的state是响应式的, 当state中的数据发生改变时, Vue组件会自动更新.<br> 这就要求我们必须遵守一些Vuex对应的规则:<br> 提前在store中初始化好所需的属性.<br> 当给state中的对象添加新属性时, 使用下面的方式:<br> 方式一: 使用Vue.set(obj, ‘newProp’, 123)<br> 方式二: 用心对象给旧对象重新赋值<br> 我们来看一个例子:<br> 当我们点击更新信息时, 界面并没有发生对应改变.</p> <pre><code class="prism language-js"> <span class="token function">updateInfo</span><span class="token punctuation">(</span><span class="token parameter">state<span class="token punctuation">,</span>payload</span><span class="token punctuation">)</span><span class="token punctuation">{</span> state<span class="token punctuation">.</span>info<span class="token punctuation">[</span><span class="token string">'height'</span><span class="token punctuation">]</span> <span class="token operator">=</span> payload<span class="token punctuation">.</span>info <span class="token punctuation">}</span> </code></pre> <pre><code class="prism language-js"> <span class="token function">updateInfo</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>$store<span class="token punctuation">.</span><span class="token function">commit</span><span class="token punctuation">(</span><span class="token string">'updateInfo'</span><span class="token punctuation">,</span><span class="token punctuation">{</span>height<span class="token operator">:</span><span class="token number">188</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> </code></pre> <p>如何才能让它改变呢?<br> 查看下面代码的方式一和方式二<br> 都可以让state中的属性是响应式的.</p> <pre><code class="prism language-js"> <span class="token function">updateInfo</span><span class="token punctuation">(</span><span class="token parameter">state<span class="token punctuation">,</span>payload</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token comment">// state.info['height'] = payload.info</span> <span class="token comment">// 方式一Vue.set</span> Vue<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span>state<span class="token punctuation">.</span>info<span class="token punctuation">,</span><span class="token string">'height'</span><span class="token punctuation">,</span>payload<span class="token punctuation">.</span>height<span class="token punctuation">)</span> <span class="token comment">// 方式二:给info赋值新的对象</span> state<span class="token punctuation">.</span>info<span class="token operator">=</span><span class="token punctuation">{</span><span class="token operator">...</span>state<span class="token punctuation">.</span>info<span class="token punctuation">,</span><span class="token string">'height'</span><span class="token operator">:</span> payload<span class="token punctuation">.</span>height<span class="token punctuation">}</span> <span class="token punctuation">}</span> </code></pre> <h6>Mutation常量类型– 概念</h6> <p>我们来考虑下面的问题:<br> 在mutation中, 我们定义了很多事件类型(也就是其中的方法名称).<br> 当我们的项目增大时, Vuex管理的状态越来越多, 需要更新状态的情况越来越多, 那么意味着Mutation中的方法越来越多.<br> 方法过多, 使用者需要花费大量的经历去记住这些方法, 甚至是多个文件间来回切换, 查看方法名称, 甚至如果不是复制的时候, 可能还会出现写错的情况.<br> 如何避免上述的问题呢?<br> 在各种Flux实现中, 一种很常见的方案就是使用常量替代Mutation事件的类型.<br> 我们可以将这些常量放在一个单独的文件中, 方便管理以及让整个app所有的事件类型一目了然.<br> 具体怎么做呢?<br> 我们可以创建一个文件: mutation-types.js, 并且在其中定义我们的常量.<br> 定义常量时, 我们可以使用ES2015中的风格, 使用一个常量来作为函数的名称.</p> <ul> <li>Mutation常量类型 – 代码</li> </ul> <p>mutation-types.js</p> <pre><code class="prism language-js"><span class="token keyword">export</span> <span class="token keyword">const</span> <span class="token constant">UPDATE_INFO</span><span class="token operator">=</span><span class="token string">'UPDATE_INFO'</span> </code></pre> <p>mutation</p> <pre><code class="prism language-js"><span class="token comment">//导入</span> <span class="token keyword">import</span> <span class="token operator">*</span> <span class="token keyword">as</span> types <span class="token keyword">from</span> <span class="token string">"./mutation-types"</span><span class="token punctuation">;</span> <span class="token punctuation">[</span>types<span class="token punctuation">.</span><span class="token constant">UPDATE_INFO</span><span class="token punctuation">]</span><span class="token punctuation">(</span><span class="token parameter">state<span class="token punctuation">,</span>payload</span><span class="token punctuation">)</span><span class="token punctuation">{</span> state<span class="token punctuation">.</span>info<span class="token operator">=</span><span class="token punctuation">{</span><span class="token operator">...</span>state<span class="token punctuation">.</span>info<span class="token punctuation">,</span><span class="token string">'height'</span><span class="token operator">:</span> payload<span class="token punctuation">.</span>height<span class="token punctuation">}</span> <span class="token punctuation">}</span> </code></pre> <p>组件中</p> <pre><code class="prism language-js"><span class="token comment">//导入 </span> <span class="token keyword">import</span> <span class="token punctuation">{</span><span class="token constant">UPDATE_INFO</span><span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"../store/mutation-types"</span><span class="token punctuation">;</span> <span class="token function">updateInfo</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>$store<span class="token punctuation">.</span><span class="token function">commit</span><span class="token punctuation">(</span><span class="token constant">UPDATE_INFO</span><span class="token punctuation">,</span><span class="token punctuation">{</span>height<span class="token operator">:</span><span class="token number">188</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> </code></pre> <h6>Mutation同步函数</h6> <p>通常情况下, Vuex要求我们Mutation中的方法必须是同步方法.<br> 主要的原因是当我们使用devtools时, 可以devtools可以帮助我们捕捉mutation的快照.<br> 但是如果是异步操作, 那么devtools将不能很好的追踪这个操作什么时候会被完成.<br> 比如我们之前的代码, 当执行更新时, devtools中会更新<br> 但是, 如果Vuex中的代码, 我们使用了异步函数:</p> <p>你会发现state中的info数据一直没有被改变, 因为他无法追踪到.<br> So, 通常情况下, 不要再mutation中进行异步的操作</p> <h6>Action的基本定义</h6> <p>我们强调, 不要再Mutation中进行异步操作.<br> 但是某些情况, 我们确实希望在Vuex中进行一些异步操作, 比如网络请求, 必然是异步的. 这个时候怎么处理呢?<br> Action类似于Mutation, 但是是用来代替Mutation进行异步操作的.<br> Action的基本使用代码如下:<br> context是什么?<br> context是和store对象具有相同方法和属性的对象.<br> 也就是说, 我们可以通过context去进行commit相关的操作, 也可以获取context.state等.<br> 但是注意, 这里它们并不是同一个对象, 为什么呢? 我们后面学习Modules的时候, 再具体说.<br> 这样的代码是否多此一举呢?<br> 我们定义了actions, 然后又在actions中去进行commit, 这不是脱裤放屁吗?<br> 事实上并不是这样, 如果在Vuex中有异步操作, 那么我们就可以在actions中完成了.</p> <p>1.在Vue组件中, 如果我们调用action中的方法, 那么就需要使用dispatch</p> <pre><code> increment(){ this.$store.dispatch('incrementAC') } </code></pre> <p>2.在action接收组件传过来的事件</p> <pre><code class="prism language-js"> actions<span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token function">incrementAC</span><span class="token punctuation">(</span><span class="token parameter">context</span><span class="token punctuation">)</span><span class="token punctuation">{</span> context<span class="token punctuation">.</span><span class="token function">commit</span><span class="token punctuation">(</span><span class="token string">'incrementMu'</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> </code></pre> <p>3.mutation中处理action提交过来的事件</p> <pre><code class="prism language-js"><span class="token function">incrementMu</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'提交成功'</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> </code></pre> <ul> <li>同样的, 也是支持传递payload</li> </ul> <p>vue组件</p> <pre><code class="prism language-js"><span class="token function">increment</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>$store<span class="token punctuation">.</span><span class="token function">dispatch</span><span class="token punctuation">(</span><span class="token string">'incrementAction'</span><span class="token punctuation">,</span><span class="token punctuation">{</span>count<span class="token operator">:</span><span class="token number">100</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> </code></pre> <p>action</p> <pre><code class="prism language-js"> actions<span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token function">incrementAction</span><span class="token punctuation">(</span><span class="token parameter">context<span class="token punctuation">,</span>payload</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token function">setTimeout</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span><span class="token punctuation">{</span> context<span class="token punctuation">.</span><span class="token function">commit</span><span class="token punctuation">(</span><span class="token string">'incremenMuta'</span><span class="token punctuation">,</span>payload<span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token number">3000</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> </code></pre> <p>mutation</p> <pre><code> incremenMuta(state,payload){ state.count += payload.count } </code></pre> <h6>Action返回的Promise</h6> <p>ES6语法中, Promise经常用于异步操作.</p> <p>在Action中, 我们可以将异步操作放在一个Promise中, 并且在成功或者失败后, 调用对应的resolve或reject.</p> <p>OK, 我们来看下面的代码:</p> <p>vue组件:</p> <pre><code class="prism language-js"> <span class="token function">increment</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token comment">// this.$store.dispatch('incrementAction',{count:100})</span> <span class="token keyword">this</span><span class="token punctuation">.</span>$store<span class="token punctuation">.</span><span class="token function">dispatch</span><span class="token punctuation">(</span><span class="token string">'incrementAction'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token parameter">res</span> <span class="token operator">=></span> <span class="token punctuation">{</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'完成了操作'</span><span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> </code></pre> <p>action:</p> <pre><code class="prism language-js"> <span class="token function">incrementAction</span><span class="token punctuation">(</span><span class="token parameter">context</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">Promise</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">resolve</span><span class="token punctuation">)</span> <span class="token operator">=></span><span class="token punctuation">{</span> <span class="token function">setTimeout</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> context<span class="token punctuation">.</span><span class="token function">commit</span><span class="token punctuation">(</span><span class="token string">'incremenMuta'</span><span class="token punctuation">)</span> <span class="token function">resolve</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token number">1000</span><span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> </code></pre> <p>mutation:</p> <pre><code class="prism language-js"><span class="token function">incremenMuta</span><span class="token punctuation">(</span><span class="token parameter">state<span class="token punctuation">,</span>payload</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token punctuation">}</span> </code></pre> <p>认识Module</p> <p>Module是模块的意思, 为什么在Vuex中我们要使用模块呢?<br> Vue使用单一状态树,那么也意味着很多状态都会交给Vuex来管理.<br> 当应用变得非常复杂时,store对象就有可能变得相当臃肿.<br> 为了解决这个问题, Vuex允许我们将store分割成模块(Module), 而每个模块拥有自己的state、mutation、action、getters等</p> <p>我们按照什么样的方式来组织模块呢?</p> <pre><code class="prism language-js"><span class="token keyword">import</span> <span class="token punctuation">{</span> createStore<span class="token punctuation">,</span> storeKey <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'vuex'</span> <span class="token keyword">const</span> moduleA<span class="token operator">=</span><span class="token punctuation">{</span> state<span class="token operator">:</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span> mutations<span class="token operator">:</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span> actions<span class="token operator">:</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span> getters<span class="token operator">:</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">}</span> <span class="token keyword">const</span> modulB<span class="token operator">=</span><span class="token punctuation">{</span> state<span class="token operator">:</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span> mutations<span class="token operator">:</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span> actions<span class="token operator">:</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span> getters<span class="token operator">:</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">}</span> <span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token function">createStore</span><span class="token punctuation">(</span><span class="token punctuation">{</span> modules<span class="token operator">:</span> <span class="token punctuation">{</span> a<span class="token operator">:</span>moduleA<span class="token punctuation">,</span> b<span class="token operator">:</span>moduleB <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> store<span class="token punctuation">.</span>state<span class="token punctuation">.</span>a <span class="token comment">//moduleA的状态</span> store<span class="token punctuation">.</span>state<span class="token punctuation">.</span>b <span class="token comment">//moduleB的状态</span> </code></pre> <h6>Module局部状态</h6> <p>上面的代码中, 我们已经有了整体的组织结构, 下面我们来看看具体的局部模块中的代码如何书写.<br> 我们在moduleA中添加state、mutations、getters<br> mutation和getters接收的第一个参数是局部状态对象</p> <pre><code class="prism language-js"> computed<span class="token operator">:</span><span class="token punctuation">{</span> <span class="token function">count</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">.</span>$store<span class="token punctuation">.</span>getters<span class="token punctuation">.</span>dobCount <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> methods<span class="token operator">:</span><span class="token punctuation">{</span> <span class="token function">increment</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>$store<span class="token punctuation">.</span><span class="token function">commit</span><span class="token punctuation">(</span><span class="token string">'increment'</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> </code></pre> <pre><code class="prism language-js"><span class="token keyword">import</span> <span class="token punctuation">{</span> createStore<span class="token punctuation">,</span> storeKey <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'vuex'</span> <span class="token keyword">const</span> moduleA<span class="token operator">=</span><span class="token punctuation">{</span> state<span class="token operator">:</span><span class="token punctuation">{</span> count<span class="token operator">:</span><span class="token number">1</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> mutations<span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token function">increment</span><span class="token punctuation">(</span><span class="token parameter">state</span><span class="token punctuation">)</span><span class="token punctuation">{</span> state<span class="token punctuation">.</span>count<span class="token operator">++</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> getters<span class="token operator">:</span><span class="token punctuation">{</span> <span class="token function">dobCount</span><span class="token punctuation">(</span><span class="token parameter">state</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">return</span> state<span class="token punctuation">.</span>count <span class="token operator">*</span> <span class="token number">2</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">const</span> moduleB<span class="token operator">=</span><span class="token punctuation">{</span> state<span class="token operator">:</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span> mutations<span class="token operator">:</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span> actions<span class="token operator">:</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span> getters<span class="token operator">:</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">}</span> <span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token function">createStore</span><span class="token punctuation">(</span><span class="token punctuation">{</span> modules<span class="token operator">:</span> <span class="token punctuation">{</span> a<span class="token operator">:</span>moduleA<span class="token punctuation">,</span> b<span class="token operator">:</span>moduleB <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> </code></pre> <ul> <li>Actions的写法</li> </ul> <p>actions的写法呢? 接收一个context参数对象</p> <p>局部状态通过 context.state 暴露出来,根节点状态则为 context.rootState</p> <pre><code class="prism language-js"> actions<span class="token operator">:</span><span class="token punctuation">{</span> <span class="token function">incrementSum</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span>state<span class="token punctuation">,</span>commit<span class="token punctuation">,</span>rootState<span class="token punctuation">}</span></span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">if</span><span class="token punctuation">(</span><span class="token punctuation">(</span>state<span class="token punctuation">.</span>count<span class="token operator">+</span>rootState<span class="token punctuation">.</span>count<span class="token punctuation">)</span> <span class="token operator">%</span> <span class="token number">2</span><span class="token operator">===</span><span class="token number">1</span> <span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token function">commit</span><span class="token punctuation">(</span><span class="token string">'increment'</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> </code></pre> <p>如果getters中也需要使用全局的状态, 可以接受更多的参数</p> <pre><code class="prism language-js"> getters<span class="token operator">:</span><span class="token punctuation">{</span> <span class="token function">sunRoot</span><span class="token punctuation">(</span><span class="token parameter">state<span class="token punctuation">,</span>getters<span class="token punctuation">,</span>rootState</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">return</span> state<span class="token punctuation">.</span>count<span class="token operator">+</span>rootState<span class="token punctuation">.</span>count <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> </code></pre> <h6>项目结构</h6> <p>当我们的Vuex帮助我们管理过多的内容时, 好的项目结构可以让我们的代码更加清晰.</p> <p>store</p> <p>|————index.js 组装并导出store的地方</p> <p>|————actions.js 根级别的actions</p> <p>|————mutations.js 根级别的mutations</p> <p>|————getters.js 根级别的getters</p> <p>|————modules</p> <p>​ |—— cart.js 购物车模块</p> <p>​ |——product.js 产品模块</p> <p>index.js</p> <pre><code class="prism language-js"><span class="token keyword">import</span> <span class="token punctuation">{</span> createStore <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'vuex'</span> <span class="token keyword">import</span> mutations <span class="token keyword">from</span> <span class="token string">'./mutations'</span> <span class="token keyword">import</span> actions <span class="token keyword">from</span> <span class="token string">'./actions'</span> <span class="token keyword">import</span> getters <span class="token keyword">from</span> <span class="token string">'./getters'</span> <span class="token keyword">const</span> state<span class="token operator">=</span><span class="token punctuation">{</span> cartList<span class="token operator">:</span><span class="token punctuation">[</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">]</span> <span class="token punctuation">}</span> <span class="token keyword">const</span> store <span class="token operator">=</span> <span class="token function">createStore</span><span class="token punctuation">(</span><span class="token punctuation">{</span> state<span class="token punctuation">,</span> getters<span class="token punctuation">,</span> mutations<span class="token punctuation">,</span> actions<span class="token punctuation">,</span> modules<span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token keyword">export</span> <span class="token keyword">default</span> store </code></pre> <p>getters.js 其他几个一样</p> <pre><code class="prism language-js"><span class="token keyword">export</span> <span class="token keyword">default</span><span class="token punctuation">{</span> <span class="token function">cartLength</span><span class="token punctuation">(</span><span class="token parameter">state</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">return</span> state<span class="token punctuation">.</span>cartList<span class="token punctuation">.</span>length <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">}</span> </code></pre> <h4>十三,网络模块封装</h4> <h5>主要内容</h5> <p>常见的网络请求模块,以及优缺点对比。<br> JSONP的原理和封装<br> JSONP原理回顾<br> JSONP请求封装<br> axios的内容详解<br> 认识axios网络模块<br> 发送基本请求<br> axios创建实例<br> axios拦截器的使用</p> <h6>选择什么网络模块?</h6> <p>Vue中发送网络请求有非常多的方式, 那么, 在开发中, 如何选择呢?</p> <p>1.选择一: 传统的Ajax是基于XMLHttpRequest(XHR)</p> <p>为什么不用它呢?<br> 非常好解释, 配置和调用方式等非常混乱.<br> 编码起来看起来就非常蛋疼.<br> 所以真实开发中很少直接使用, 而是使用jQuery-Aja</p> <p>2.选择二: 在前面的学习中, 我们经常会使用jQuery-Ajax</p> <p>相对于传统的Ajax非常好用.<br> 为什么不选择它呢?<br> 首先, 我们先明确一点: 在Vue的整个开发中都是不需要使用jQuery了.<br> 那么, 就意味着为了方便我们进行一个网络请求, 特意引用一个jQuery, 你觉得合理吗?<br> jQuery的代码1w+行.<br> Vue的代码才1w+行.<br> 完全没有必要为了用网络请求就引用这个重量级的框架.<br> 3.选择三: 官方在Vue1.x的时候, 推出了Vue-resource.</p> <p>Vue-resource的体积相对于jQuery小很多.<br> 另外Vue-resource是官方推出的.<br> 为什么不选择它呢?<br> 在Vue2.0退出后, Vue作者就在GitHub的Issues中说明了去掉vue-resource, 并且以后也不会再更新.<br> 那么意味着以后vue-reource不再支持新的版本时, 也不会再继续更新和维护.<br> 对以后的项目开发和维护都存在很大的隐患.<br> 4.选择四: 在说明不再继续更新和维护vue-resource的同时, 作者还推荐了一个框架: axios为什么不用它呢?</p> <p>axios有非常多的优点, 并且用起来也非常方便.<br> 稍后, 我们对他详细学习.</p> <h6>jsonp</h6> <p>在前端开发中, 我们一种常见的网络请求方式就是JSONP<br> 使用JSONP最主要的原因往往是为了解决跨域访问的问题.<br> JSONP的原理是什么呢?<br> JSONP的核心在于通过</p> <h6>为什么选择axios?</h6> <p>作者推荐和功能特点</p> <p>2016年11月作者公布axios</p> <p>功能特点:<br> 在浏览器中发送 XMLHttpRequests 请求<br> 在 node.js 中发送 http请求<br> 支持 Promise API<br> 拦截请求和响应<br> 转换请求和响应数据<br> 等等</p> <h6>axiox请求方式</h6> <p>支持多种请求方式:<br> axios(config)<br> axios.request(config)<br> axios.get(url[, config])<br> axios.delete(url[, config])<br> axios.head(url[, config])<br> axios.post(url[, data[, config]])<br> axios.put(url[, data[, config]])<br> axios.patch(url[, data[, config]])<br> 如何发送请求呢?<br> 我们看一下左边的案例</p> <h6>安装使用</h6> <p>安装axios</p> <pre><code>cnpm install axios --save </code></pre> <p>引入</p> <pre><code>import Axios from 'axios'; </code></pre> <h6>发送get请求演示</h6> <pre><code class="prism language-vue"><template> </template> <script> import Axios from 'axios'; export default { name:"AXios_stu", created(){ //为什么这里没有跨域问题 //1.没有请求参数 Axios.get('http://123.207.32.32:8000/category') .then(res => { console.log(res); }).catch(err => { console.log(err) }) //2.有参数请求 Axios.get('http://123.207.32.32:8000/home/data', {params:{type:'sell',page:1}}) .then(res => { console.log(res) }).catch( err => { console.log(err) }) }, } </script> </code></pre> <h6>发送并发请求</h6> <p>有时候, 我们可能需求同时发送两个请求</p> <p>使用axios.all, 可以放入多个请求的数组.</p> <p>axios.all([]) 返回的结果是一个数组,使用 axios.spread 可将数组 [res1,res2] 展开为 res1, res2</p> <pre><code class="prism language-js"><span class="token comment">//发送并发请求</span> axios<span class="token punctuation">.</span><span class="token function">all</span><span class="token punctuation">(</span><span class="token punctuation">[</span> axios<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'http://123.207.32.32:8000/category'</span><span class="token punctuation">)</span><span class="token punctuation">,</span> axios<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'http://123.207.32.32:8000/home/data'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>params<span class="token operator">:</span><span class="token punctuation">{</span>type<span class="token operator">:</span><span class="token string">'sell'</span><span class="token punctuation">,</span>page<span class="token operator">:</span><span class="token number">1</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span>axios<span class="token punctuation">.</span><span class="token function">spread</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">res1<span class="token punctuation">,</span>res2</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>res1<span class="token punctuation">)</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>res2<span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">)</span> </code></pre> <h6>全局配置</h6> <p>在上面的示例中, 我们的BaseURL是固定的</p> <p>事实上, 在开发中可能很多参数都是固定的.</p> <p>这个时候我们可以进行一些抽取, 也可以利用axiox的全局配置</p> <pre><code class="prism language-js">axios<span class="token punctuation">.</span>defaults<span class="token punctuation">.</span>baseURL <span class="token operator">=</span> ‘<span class="token number">123.207</span><span class="token number">.32</span><span class="token number">.32</span><span class="token operator">:</span><span class="token number">8000</span>’ axios<span class="token punctuation">.</span>defaults<span class="token punctuation">.</span>headers<span class="token punctuation">.</span>post<span class="token punctuation">[</span>‘Content<span class="token operator">-</span>Type’<span class="token punctuation">]</span> <span class="token operator">=</span> ‘application<span class="token operator">/</span>x<span class="token operator">-</span>www<span class="token operator">-</span>form<span class="token operator">-</span>urlencoded’<span class="token punctuation">;</span> </code></pre> <pre><code class="prism language-js"> <span class="token comment">//提取全局配置</span> axios<span class="token punctuation">.</span>defaults<span class="token punctuation">.</span>baseURL <span class="token operator">=</span> <span class="token string">'http://123.207.32.32:8000'</span> <span class="token comment">//发送并发请求</span> axios<span class="token punctuation">.</span><span class="token function">all</span><span class="token punctuation">(</span><span class="token punctuation">[</span> axios<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'/category'</span><span class="token punctuation">)</span><span class="token punctuation">,</span> axios<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'/home/data'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>params<span class="token operator">:</span><span class="token punctuation">{</span>type<span class="token operator">:</span><span class="token string">'sell'</span><span class="token punctuation">,</span>page<span class="token operator">:</span><span class="token number">1</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span>axios<span class="token punctuation">.</span><span class="token function">spread</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">res1<span class="token punctuation">,</span>res2</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>res1<span class="token punctuation">)</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>res2<span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">)</span> </code></pre> <h6>常见的配置选项</h6> <p>1.请求地址</p> <pre><code>url: '/user', </code></pre> <p>2.请求类型</p> <pre><code> method: 'get', </code></pre> <p>3.请根路径</p> <pre><code>baseURL: 'http://www.mt.com/api', </code></pre> <p>5.请求前的数据处理</p> <p>4.请求前的数据处理</p> <pre><code>transformRequest:[function(data){}], </code></pre> <p>6.请求后的数据处理</p> <pre><code>transformResponse: [function(data){}], </code></pre> <p>7.自定义的请求头</p> <pre><code>headers:{'x-Requested-With':'XMLHttpRequest'}, </code></pre> <p>8.URL查询对象</p> <pre><code>params:{ id: 12 }, </code></pre> <p>9.查询对象序列化函数</p> <pre><code>paramsSerializer: function(params){ } request body data: { key: 'aa'}, </code></pre> <p>10.超时设置s</p> <pre><code>timeout: 1000, </code></pre> <p>11.跨域是否带Token</p> <pre><code>withCredentials: false, </code></pre> <p>12.自定义请求处理</p> <pre><code>adapter: function(resolve, reject, config){}, </code></pre> <p>13.身份验证信息</p> <pre><code>auth: { uname: '', pwd: '12'}, </code></pre> <p>14.响应的数据格式 json / blob /document /arraybuffer / text / stream</p> <pre><code>responseType: 'json', </code></pre> <h6>axios的实例</h6> <p>为什么要创建axios的实例呢?<br> 当我们从axios模块中导入对象时, 使用的实例是默认的实例.<br> 当给该实例设置一些默认配置时, 这些配置就被固定下来了.<br> 但是后续开发中, 某些配置可能会不太一样.<br> 比如某些请求需要使用特定的baseURL或者timeout或者content-Type等.<br> 这个时候, 我们就可以创建新的实例, 并且传入属于该实例的配置信息</p> <pre><code class="prism language-js"> <span class="token comment">//创建新的实例</span> <span class="token keyword">const</span> axiosInstance <span class="token operator">=</span> axios<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token punctuation">{</span> baseURL <span class="token operator">:</span> <span class="token string">'http://123.207.32.32:8000'</span><span class="token punctuation">,</span> timeout<span class="token operator">:</span><span class="token number">3000</span><span class="token punctuation">,</span> headers<span class="token operator">:</span><span class="token punctuation">{</span> <span class="token string">'Content-Type'</span><span class="token operator">:</span><span class="token string">'application/x-www-form-urlencoded'</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token function">axiosInstance</span><span class="token punctuation">(</span><span class="token punctuation">{</span> url<span class="token operator">:</span><span class="token string">'/category'</span><span class="token punctuation">,</span> method<span class="token operator">:</span><span class="token string">'get'</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span> <span class="token parameter">res</span> <span class="token operator">=></span> <span class="token punctuation">{</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>res<span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">catch</span><span class="token punctuation">(</span> <span class="token parameter">err</span> <span class="token operator">=></span> <span class="token punctuation">{</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> </code></pre> <h5>axios封装</h5> <pre><code class="prism language-js"><span class="token keyword">import</span> OriginAxios <span class="token keyword">from</span> <span class="token string">'axios'</span><span class="token punctuation">;</span> <span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">function</span> <span class="token function">axios</span> <span class="token punctuation">(</span><span class="token parameter">option</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">Promise</span> <span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">resolve<span class="token punctuation">,</span>reject</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">//1,创建axios的实例</span> <span class="token keyword">const</span> instance <span class="token operator">=</span> OriginAxios<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token punctuation">{</span> baseURl<span class="token operator">:</span><span class="token string">'/api'</span><span class="token punctuation">,</span> timeout<span class="token operator">:</span><span class="token number">5000</span><span class="token punctuation">,</span> headers<span class="token operator">:</span><span class="token string">''</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token comment">//2,传入对象进行网络请求</span> <span class="token function">instance</span><span class="token punctuation">(</span>option<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token parameter">res</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token function">resolve</span> <span class="token punctuation">(</span>res<span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">catch</span><span class="token punctuation">(</span> <span class="token parameter">err</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token function">reject</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> </code></pre> <p>axios本身返回的就是一个promise,所以promise在这里是多余的,请求用以下方法就行</p> <p>把instance返回,相当与返回promise</p> <pre><code class="prism language-js"><span class="token keyword">import</span> OriginAxios <span class="token keyword">from</span> <span class="token string">'axios'</span><span class="token punctuation">;</span> <span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">function</span> <span class="token function">axios</span> <span class="token punctuation">(</span><span class="token parameter">config</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token comment">// return new Promise ((resolve,reject) => {</span> <span class="token comment">//1,创建axios的实例</span> <span class="token keyword">const</span> instance <span class="token operator">=</span> OriginAxios<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token punctuation">{</span> baseURl<span class="token operator">:</span><span class="token string">'http://123.207.32.32:8000'</span><span class="token punctuation">,</span> timeout<span class="token operator">:</span><span class="token number">5000</span><span class="token punctuation">,</span> headers<span class="token operator">:</span><span class="token string">''</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token comment">// 2.发送真正的网络请求</span> <span class="token keyword">return</span> <span class="token function">instance</span><span class="token punctuation">(</span>config<span class="token punctuation">)</span> <span class="token punctuation">}</span> </code></pre> <p>组件中使用</p> <pre><code class="prism language-js"><span class="token keyword">import</span> axiosConfig <span class="token keyword">from</span> <span class="token string">'../axios'</span> <span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span> name<span class="token operator">:</span><span class="token string">"AXios_stu"</span><span class="token punctuation">,</span> <span class="token function">created</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token function">axiosConfig</span><span class="token punctuation">(</span><span class="token punctuation">{</span> url<span class="token operator">:</span><span class="token string">'/category'</span><span class="token punctuation">,</span> methods<span class="token operator">:</span><span class="token string">'get'</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span> <span class="token parameter">res</span> <span class="token operator">=></span> <span class="token punctuation">{</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>res<span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">catch</span><span class="token punctuation">(</span> <span class="token parameter">err</span> <span class="token operator">=></span> <span class="token punctuation">{</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> </code></pre> <h6>如何使用拦截器?</h6> <p>axios提供了拦截器,用于我们在发送每次请求或者得到相应后,进行对应的处理。</p> <p>如何使用拦截器呢?</p> <pre><code class="prism language-js"><span class="token keyword">import</span> OriginAxios <span class="token keyword">from</span> <span class="token string">'axios'</span><span class="token punctuation">;</span> <span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">function</span> <span class="token function">axios</span> <span class="token punctuation">(</span><span class="token parameter">config</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token comment">//1,创建axios的实例</span> <span class="token keyword">const</span> instance <span class="token operator">=</span> OriginAxios<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token punctuation">{</span> baseURl<span class="token operator">:</span><span class="token string">'http://123.207.32.32:8000'</span><span class="token punctuation">,</span> timeout<span class="token operator">:</span><span class="token number">5000</span><span class="token punctuation">,</span> headers<span class="token operator">:</span><span class="token string">''</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token comment">//2.配置请求和响应拦截</span> instance<span class="token punctuation">.</span>interceptors<span class="token punctuation">.</span>request<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span><span class="token parameter">config</span> <span class="token operator">=></span> <span class="token punctuation">{</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'来到了request拦截success中'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> config <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token parameter">err</span> <span class="token operator">=></span> <span class="token punctuation">{</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'来到了request拦截failure中'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> err <span class="token punctuation">}</span><span class="token punctuation">)</span> instance<span class="token punctuation">.</span>interceptors<span class="token punctuation">.</span>response<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span><span class="token parameter">response</span> <span class="token operator">=></span> <span class="token punctuation">{</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'来到了response拦截success中'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> response <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token parameter">err</span> <span class="token operator">=></span> <span class="token punctuation">{</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'来到了response拦截failure中'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> err <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token comment">// 3.发送真正的网络请求</span> <span class="token keyword">return</span> <span class="token function">instance</span><span class="token punctuation">(</span>config<span class="token punctuation">)</span> <span class="token punctuation">}</span> </code></pre> <h6>拦截器中都做什么呢?</h6> <p>请求拦截可以做到的事情:</p> <p>请求拦截中错误拦截较少,通常都是配置相关的拦截</p> <p>可能的错误比如请求超时,可以将页面跳转到一个错误页面中。</p> <pre><code class="prism language-js"> <span class="token comment">//配置请求拦截</span> instance<span class="token punctuation">.</span>interceptors<span class="token punctuation">.</span>request<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span><span class="token parameter">config</span> <span class="token operator">=></span> <span class="token punctuation">{</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'来到了request拦截success中'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 1.当发送网络请求是,在页面添加添加一个loading组件,作为动画</span> <span class="token comment">//2.某些要求用户登录时,判断是否有token,如果没有跳转到login页面</span> <span class="token comment">//3.对求请求的数据进行序列化</span> config<span class="token punctuation">.</span>data <span class="token operator">=</span>qs<span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span>config<span class="token punctuation">.</span>data<span class="token punctuation">)</span> <span class="token keyword">return</span> config <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token parameter">err</span> <span class="token operator">=></span> <span class="token punctuation">{</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'来到了request拦截failure中'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> err <span class="token punctuation">}</span><span class="token punctuation">)</span> </code></pre> <p>响应拦截中完成的事情:</p> <p>响应的成功拦截中,主要是对数据进行过滤。</p> <p>响应的失败拦截中,可以根据status判断报错的错误码,跳转到不同的错误提示页面。</p> <pre><code class="prism language-js"> <span class="token comment">// 配置响应拦截</span> instance<span class="token punctuation">.</span>interceptors<span class="token punctuation">.</span>response<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span><span class="token parameter">response</span> <span class="token operator">=></span> <span class="token punctuation">{</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'来到了response拦截success中'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> response<span class="token punctuation">.</span>data <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token parameter">err</span> <span class="token operator">=></span> <span class="token punctuation">{</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'来到了response拦截failure中'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span><span class="token punctuation">(</span>err <span class="token operator">&&</span> err<span class="token punctuation">.</span>response<span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">switch</span><span class="token punctuation">(</span>err<span class="token punctuation">.</span>response<span class="token punctuation">.</span>status<span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">case</span> <span class="token number">400</span><span class="token operator">:</span> err<span class="token punctuation">.</span>message <span class="token operator">=</span> <span class="token string">'请求错误'</span> <span class="token keyword">break</span> <span class="token keyword">case</span> <span class="token number">401</span> <span class="token operator">:</span> err<span class="token punctuation">.</span>message <span class="token operator">=</span> <span class="token string">'未授权的访问'</span> <span class="token keyword">break</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> err <span class="token punctuation">}</span><span class="token punctuation">)</span> </code></pre> <h6>引入element-ui</h6> <p>vue-cli4.5创建Vue3.x引入element-ui</p> <p>在Vue3项目根目录下</p> <pre><code>vue add element-plus npm install vue-cli-plugin-element-plus </code></pre> <p>全局引用 -自动生成</p> <p>src/plugins/element.js</p> <pre><code>import ElementPlus from 'element-plus' import 'element-plus/lib/theme-chalk/index.css' export default (app) => { app.use(ElementPlus) } </code></pre> <p>全局按需引入</p> <pre><code>import { createApp } from 'vue' import App from './App.vue' import 'element-plus/lib/theme-chalk/index.css' import { ElButton, ElSelect } from 'element-plus'; const app = createApp(App) app.use(ElButton) app.use(ElSelect) app.mount('#app') </code></pre> <p>注意App.vue会被重置,注意另存再整合</p> <h6>拦截器loading</h6> <p>element-ui制作一个axios拦截器loading</p> <pre><code class="prism language-js"><span class="token keyword">import</span> OriginAxios <span class="token keyword">from</span> <span class="token string">'axios'</span><span class="token punctuation">;</span> <span class="token keyword">import</span> <span class="token punctuation">{</span> ElLoading <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'element-plus'</span><span class="token punctuation">;</span> <span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">function</span> <span class="token function">axios</span> <span class="token punctuation">(</span><span class="token parameter">config</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token comment">//1,创建axios的实例</span> <span class="token keyword">const</span> instance <span class="token operator">=</span> OriginAxios<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token punctuation">{</span> baseURl<span class="token operator">:</span><span class="token string">'http://123.207.32.32:8000'</span><span class="token punctuation">,</span> timeout<span class="token operator">:</span><span class="token number">5000</span><span class="token punctuation">,</span> headers<span class="token operator">:</span><span class="token string">''</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token comment">//定义loading变量</span> <span class="token keyword">let</span> loadingInstance <span class="token comment">//配置请求拦截</span> instance<span class="token punctuation">.</span>interceptors<span class="token punctuation">.</span>request<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span><span class="token parameter">config</span> <span class="token operator">=></span> <span class="token punctuation">{</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'来到了request拦截success中'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 1.当发送网络请求是,在页面添加添加一个loading组件,作为动画</span> loadingInstance<span class="token operator">=</span> ElLoading<span class="token punctuation">.</span><span class="token function">service</span><span class="token punctuation">(</span><span class="token punctuation">{</span> fullscreen<span class="token operator">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> config <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token parameter">err</span> <span class="token operator">=></span> <span class="token punctuation">{</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'来到了request拦截failure中 '</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> err <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token comment">// 配置响应拦截</span> instance<span class="token punctuation">.</span>interceptors<span class="token punctuation">.</span>response<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span><span class="token parameter">response</span> <span class="token operator">=></span> <span class="token punctuation">{</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'来到了response拦截success中'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//响应拦截成功,如果loading存在,则关闭loading</span> <span class="token keyword">if</span><span class="token punctuation">(</span>loadingInstance<span class="token punctuation">)</span><span class="token punctuation">{</span> loadingInstance<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> response<span class="token punctuation">.</span>data <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token parameter">err</span> <span class="token operator">=></span> <span class="token punctuation">{</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'来到了response拦截failure中'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span><span class="token punctuation">(</span>err <span class="token operator">&&</span> err<span class="token punctuation">.</span>response<span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">switch</span><span class="token punctuation">(</span>err<span class="token punctuation">.</span>response<span class="token punctuation">.</span>status<span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">case</span> <span class="token number">400</span><span class="token operator">:</span> err<span class="token punctuation">.</span>message <span class="token operator">=</span> <span class="token string">'请求错误'</span> <span class="token keyword">break</span> <span class="token keyword">case</span> <span class="token number">401</span> <span class="token operator">:</span> err<span class="token punctuation">.</span>message <span class="token operator">=</span> <span class="token string">'未授权的访问'</span> <span class="token keyword">break</span> <span class="token keyword">case</span> <span class="token number">404</span> <span class="token operator">:</span> err<span class="token punctuation">.</span>message <span class="token operator">=</span> <span class="token string">'网页错误'</span> <span class="token keyword">break</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> err <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token comment">// 3.发送真正的网络请求</span> <span class="token keyword">return</span> <span class="token function">instance</span><span class="token punctuation">(</span>config<span class="token punctuation">)</span> <span class="token punctuation">}</span> </code></pre> <h5>图片本地路径放在data中</h5> <p>要加require才能识别</p> <pre><code> list: [ {old_image:require('../../assets/img/home/shanghai-pass.jpg'),}, { id: "123" }], </code></pre></li> </ul> </div> </div> </div> </div> </div> <!--PC和WAP自适应版--> <div id="SOHUCS" sid="1498621913801490432"></div> <script type="text/javascript" src="/views/front/js/chanyan.js"></script> <!-- 文章页-底部 动态广告位 --> <div class="youdao-fixed-ad" id="detail_ad_bottom"></div> </div> <div class="col-md-3"> <div class="row" id="ad"> <!-- 文章页-右侧1 动态广告位 --> <div id="right-1" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad"> <div class="youdao-fixed-ad" id="detail_ad_1"> </div> </div> <!-- 文章页-右侧2 动态广告位 --> <div id="right-2" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad"> <div class="youdao-fixed-ad" id="detail_ad_2"></div> </div> <!-- 文章页-右侧3 动态广告位 --> <div id="right-3" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad"> <div class="youdao-fixed-ad" id="detail_ad_3"></div> </div> </div> </div> </div> </div> </div> <div class="container"> <h4 class="pt20 mb15 mt0 border-top">你可能感兴趣的:(vue.js,javascript,前端)</h4> <div id="paradigm-article-related"> <div class="recommend-post mb30"> <ul class="widget-links"> <li><a href="/article/1835512920797179904.htm" title="element实现动态路由+面包屑" target="_blank">element实现动态路由+面包屑</a> <span class="text-muted">软件技术NINI</span> <a class="tag" taget="_blank" href="/search/vue%E6%A1%88%E4%BE%8B/1.htm">vue案例</a><a class="tag" taget="_blank" href="/search/vue.js/1.htm">vue.js</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a> <div>el-breadcrumb是ElementUI组件库中的一个面包屑导航组件,它用于显示当前页面的路径,帮助用户快速理解和导航到应用的各个部分。在Vue.js项目中,如果你已经安装了ElementUI,就可以很方便地使用el-breadcrumb组件。以下是一个基本的使用示例:安装ElementUI(如果你还没有安装的话):你可以通过npm或yarn来安装ElementUI。bash复制代码npmi</div> </li> <li><a href="/article/1835509897106649088.htm" title="Long类型前后端数据不一致" target="_blank">Long类型前后端数据不一致</a> <span class="text-muted">igotyback</span> <a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a> <div>响应给前端的数据浏览器控制台中response中看到的Long类型的数据是正常的到前端数据不一致前后端数据类型不匹配是一个常见问题,尤其是当后端使用Java的Long类型(64位)与前端JavaScript的Number类型(最大安全整数为2^53-1,即16位)进行数据交互时,很容易出现精度丢失的问题。这是因为JavaScript中的Number类型无法安全地表示超过16位的整数。为了解决这个问</div> </li> <li><a href="/article/1835498925755297792.htm" title="DIV+CSS+JavaScript技术制作网页(旅游主题网页设计与制作)云南大理" target="_blank">DIV+CSS+JavaScript技术制作网页(旅游主题网页设计与制作)云南大理</a> <span class="text-muted">STU学生网页设计</span> <a class="tag" taget="_blank" href="/search/%E7%BD%91%E9%A1%B5%E8%AE%BE%E8%AE%A1/1.htm">网页设计</a><a class="tag" taget="_blank" href="/search/%E6%9C%9F%E6%9C%AB%E7%BD%91%E9%A1%B5%E4%BD%9C%E4%B8%9A/1.htm">期末网页作业</a><a class="tag" taget="_blank" href="/search/html%E9%9D%99%E6%80%81%E7%BD%91%E9%A1%B5/1.htm">html静态网页</a><a class="tag" taget="_blank" href="/search/html5%E6%9C%9F%E6%9C%AB%E5%A4%A7%E4%BD%9C%E4%B8%9A/1.htm">html5期末大作业</a><a class="tag" taget="_blank" href="/search/%E7%BD%91%E9%A1%B5%E8%AE%BE%E8%AE%A1/1.htm">网页设计</a><a class="tag" taget="_blank" href="/search/web%E5%A4%A7%E4%BD%9C%E4%B8%9A/1.htm">web大作业</a> <div>️精彩专栏推荐作者主页:【进入主页—获取更多源码】web前端期末大作业:【HTML5网页期末作业(1000套)】程序员有趣的告白方式:【HTML七夕情人节表白网页制作(110套)】文章目录二、网站介绍三、网站效果▶️1.视频演示2.图片演示四、网站代码HTML结构代码CSS样式代码五、更多源码二、网站介绍网站布局方面:计划采用目前主流的、能兼容各大主流浏览器、显示效果稳定的浮动网页布局结构。网站程</div> </li> <li><a href="/article/1835497792265613312.htm" title="【加密社】Solidity 中的事件机制及其应用" target="_blank">【加密社】Solidity 中的事件机制及其应用</a> <span class="text-muted">加密社</span> <a class="tag" taget="_blank" href="/search/%E9%97%B2%E4%BE%83/1.htm">闲侃</a><a class="tag" taget="_blank" href="/search/%E5%8C%BA%E5%9D%97%E9%93%BE/1.htm">区块链</a><a class="tag" taget="_blank" href="/search/%E6%99%BA%E8%83%BD%E5%90%88%E7%BA%A6/1.htm">智能合约</a><a class="tag" taget="_blank" href="/search/%E5%8C%BA%E5%9D%97%E9%93%BE/1.htm">区块链</a> <div>加密社引言在Solidity合约开发过程中,事件(Events)是一种非常重要的机制。它们不仅能够让开发者记录智能合约的重要状态变更,还能够让外部系统(如前端应用)监听这些状态的变化。本文将详细介绍Solidity中的事件机制以及如何利用不同的手段来触发、监听和获取这些事件。事件存储的地方当我们在Solidity合约中使用emit关键字触发事件时,该事件会被记录在区块链的交易收据中。具体而言,事件</div> </li> <li><a href="/article/1835496149843275776.htm" title="关于城市旅游的HTML网页设计——(旅游风景云南 5页)HTML+CSS+JavaScript" target="_blank">关于城市旅游的HTML网页设计——(旅游风景云南 5页)HTML+CSS+JavaScript</a> <span class="text-muted">二挡起步</span> <a class="tag" taget="_blank" href="/search/web%E5%89%8D%E7%AB%AF%E6%9C%9F%E6%9C%AB%E5%A4%A7%E4%BD%9C%E4%B8%9A/1.htm">web前端期末大作业</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/html/1.htm">html</a><a class="tag" taget="_blank" href="/search/css/1.htm">css</a><a class="tag" taget="_blank" href="/search/%E6%97%85%E6%B8%B8/1.htm">旅游</a><a class="tag" taget="_blank" href="/search/%E9%A3%8E%E6%99%AF/1.htm">风景</a> <div>⛵源码获取文末联系✈Web前端开发技术描述网页设计题材,DIV+CSS布局制作,HTML+CSS网页设计期末课程大作业|游景点介绍|旅游风景区|家乡介绍|等网站的设计与制作|HTML期末大学生网页设计作业,Web大学生网页HTML:结构CSS:样式在操作方面上运用了html5和css3,采用了div+css结构、表单、超链接、浮动、绝对定位、相对定位、字体样式、引用视频等基础知识JavaScrip</div> </li> <li><a href="/article/1835496148601761792.htm" title="HTML网页设计制作大作业(div+css) 云南我的家乡旅游景点 带文字滚动" target="_blank">HTML网页设计制作大作业(div+css) 云南我的家乡旅游景点 带文字滚动</a> <span class="text-muted">二挡起步</span> <a class="tag" taget="_blank" href="/search/web%E5%89%8D%E7%AB%AF%E6%9C%9F%E6%9C%AB%E5%A4%A7%E4%BD%9C%E4%B8%9A/1.htm">web前端期末大作业</a><a class="tag" taget="_blank" href="/search/web%E8%AE%BE%E8%AE%A1%E7%BD%91%E9%A1%B5%E8%A7%84%E5%88%92%E4%B8%8E%E8%AE%BE%E8%AE%A1/1.htm">web设计网页规划与设计</a><a class="tag" taget="_blank" href="/search/html/1.htm">html</a><a class="tag" taget="_blank" href="/search/css/1.htm">css</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/dreamweaver/1.htm">dreamweaver</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a> <div>Web前端开发技术描述网页设计题材,DIV+CSS布局制作,HTML+CSS网页设计期末课程大作业游景点介绍|旅游风景区|家乡介绍|等网站的设计与制作HTML期末大学生网页设计作业HTML:结构CSS:样式在操作方面上运用了html5和css3,采用了div+css结构、表单、超链接、浮动、绝对定位、相对定位、字体样式、引用视频等基础知识JavaScript:做与用户的交互行为文章目录前端学习路线</div> </li> <li><a href="/article/1835492740536823808.htm" title="node.js学习" target="_blank">node.js学习</a> <span class="text-muted">小猿L</span> <a class="tag" taget="_blank" href="/search/node.js/1.htm">node.js</a><a class="tag" taget="_blank" href="/search/node.js/1.htm">node.js</a><a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0/1.htm">学习</a><a class="tag" taget="_blank" href="/search/vim/1.htm">vim</a> <div>node.js学习实操及笔记温故node.js,node.js学习实操过程及笔记~node.js学习视频node.js官网node.js中文网实操笔记githubcsdn笔记为什么学node.js可以让别人访问我们编写的网页为后续的框架学习打下基础,三大框架vuereactangular离不开node.jsnode.js是什么官网:node.js是一个开源的、跨平台的运行JavaScript的运行</div> </li> <li><a href="/article/1835448238103162880.htm" title="springboot+vue项目实战一-创建SpringBoot简单项目" target="_blank">springboot+vue项目实战一-创建SpringBoot简单项目</a> <span class="text-muted">苹果酱0567</span> <a class="tag" taget="_blank" href="/search/%E9%9D%A2%E8%AF%95%E9%A2%98%E6%B1%87%E6%80%BB%E4%B8%8E%E8%A7%A3%E6%9E%90/1.htm">面试题汇总与解析</a><a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/boot/1.htm">boot</a><a class="tag" taget="_blank" href="/search/%E5%90%8E%E7%AB%AF/1.htm">后端</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E4%B8%AD%E9%97%B4%E4%BB%B6/1.htm">中间件</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a> <div>这段时间抽空给女朋友搭建一个个人博客,想着记录一下建站的过程,就当做笔记吧。虽然复制zjblog只要一个小时就可以搞定一个网站,或者用cms系统,三四个小时就可以做出一个前后台都有的网站,而且想做成啥样也都行。但是就是要从新做,自己做的意义不一样,更何况,俺就是专门干这个的,嘿嘿嘿要做一个网站,而且从零开始,首先呢就是技术选型了,经过一番思量决定选择-SpringBoot做后端,前端使用Vue做一</div> </li> <li><a href="/article/1835448239864770560.htm" title="JavaScript 中,深拷贝(Deep Copy)和浅拷贝(Shallow Copy)" target="_blank">JavaScript 中,深拷贝(Deep Copy)和浅拷贝(Shallow Copy)</a> <span class="text-muted">跳房子的前端</span> <a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/1.htm">前端面试</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/ecmascript/1.htm">ecmascript</a> <div>在JavaScript中,深拷贝(DeepCopy)和浅拷贝(ShallowCopy)是用于复制对象或数组的两种不同方法。了解它们的区别和应用场景对于避免潜在的bugs和高效地处理数据非常重要。以下是对深拷贝和浅拷贝的详细解释,包括它们的概念、用途、优缺点以及实现方式。1.浅拷贝(ShallowCopy)概念定义:浅拷贝是指创建一个新的对象或数组,其中包含了原对象或数组的基本数据类型的值和对引用数</div> </li> <li><a href="/article/1835437775344726016.htm" title="博客网站制作教程" target="_blank">博客网站制作教程</a> <span class="text-muted">2401_85194651</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/maven/1.htm">maven</a> <div>首先就是技术框架:后端:Java+SpringBoot数据库:MySQL前端:Vue.js数据库连接:JPA(JavaPersistenceAPI)1.项目结构blog-app/├──backend/│├──src/main/java/com/example/blogapp/││├──BlogApplication.java││├──config/│││└──DatabaseConfig.java</div> </li> <li><a href="/article/1835428948339683328.htm" title="JavaScript `Map` 和 `WeakMap`详细解释" target="_blank">JavaScript `Map` 和 `WeakMap`详细解释</a> <span class="text-muted">跳房子的前端</span> <a class="tag" taget="_blank" href="/search/JavaScript/1.htm">JavaScript</a><a class="tag" taget="_blank" href="/search/%E5%8E%9F%E7%94%9F%E6%96%B9%E6%B3%95/1.htm">原生方法</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a> <div>在JavaScript中,Map和WeakMap都是用于存储键值对的数据结构,但它们有一些关键的不同之处。MapMap是一种可以存储任意类型的键值对的集合。它保持了键值对的插入顺序,并且可以通过键快速查找对应的值。Map提供了一些非常有用的方法和属性来操作这些数据对:set(key,value):将一个键值对添加到Map中。如果键已经存在,则更新其对应的值。get(key):获取指定键的值。如果键</div> </li> <li><a href="/article/1835428317084348416.htm" title="最简单将静态网页挂载到服务器上(不用nginx)" target="_blank">最简单将静态网页挂载到服务器上(不用nginx)</a> <span class="text-muted">全能全知者</span> <a class="tag" taget="_blank" href="/search/%E6%9C%8D%E5%8A%A1%E5%99%A8/1.htm">服务器</a><a class="tag" taget="_blank" href="/search/nginx/1.htm">nginx</a><a class="tag" taget="_blank" href="/search/%E8%BF%90%E7%BB%B4/1.htm">运维</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/html/1.htm">html</a><a class="tag" taget="_blank" href="/search/%E7%AC%94%E8%AE%B0/1.htm">笔记</a> <div>最简单将静态网页挂载到服务器上(不用nginx)如果随便弄个静态网页挂在服务器都要用nignx就太麻烦了,所以直接使用Apache来搭建一些简单前端静态网页会相对方便很多检查Web服务器服务状态:sudosystemctlstatushttpd#ApacheWeb服务器如果发现没有安装web服务器:安装Apache:sudoyuminstallhttpd启动Apache:sudosystemctl</div> </li> <li><a href="/article/1835427057752961024.htm" title="补充元象二面" target="_blank">补充元象二面</a> <span class="text-muted">Redstone Monstrosity</span> <a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/%E9%9D%A2%E8%AF%95/1.htm">面试</a> <div>1.请尽可能详细地说明,防抖和节流的区别,应用场景?你的回答中不要写出示例代码。防抖(Debounce)和节流(Throttle)是两种常用的前端性能优化技术,它们的主要区别在于如何处理高频事件的触发。以下是防抖和节流的区别和应用场景的详细说明:防抖和节流的定义防抖:在一段时间内,多次执行变为只执行最后一次。防抖的原理是,当事件被触发后,设置一个延迟定时器。如果在这个延迟时间内事件再次被触发,则重</div> </li> <li><a href="/article/1835420753252675584.htm" title="微信小程序开发注意事项" target="_blank">微信小程序开发注意事项</a> <span class="text-muted">jun778895</span> <a class="tag" taget="_blank" href="/search/%E5%BE%AE%E4%BF%A1%E5%B0%8F%E7%A8%8B%E5%BA%8F/1.htm">微信小程序</a><a class="tag" taget="_blank" href="/search/%E5%B0%8F%E7%A8%8B%E5%BA%8F/1.htm">小程序</a> <div>微信小程序开发是一个融合了前端开发、用户体验设计、后端服务(可选)以及微信小程序平台特性的综合性项目。这里,我将详细介绍一个典型的小程序开发项目的全过程,包括项目规划、设计、开发、测试及部署上线等各个环节,并尽量使内容达到或超过2000字的要求。一、项目规划1.1项目背景与目标假设我们要开发一个名为“智慧校园助手”的微信小程序,旨在为学生提供一站式校园生活服务,包括课程表查询、图书馆座位预约、食堂</div> </li> <li><a href="/article/1835419870070665216.htm" title="切换淘宝最新npm镜像源是" target="_blank">切换淘宝最新npm镜像源是</a> <span class="text-muted">hai40587</span> <a class="tag" taget="_blank" href="/search/npm/1.htm">npm</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/node.js/1.htm">node.js</a> <div>切换淘宝最新npm镜像源是一个相对简单的过程,但首先需要明确当前淘宝npm镜像源的状态和最新的镜像地址。由于网络环境和服务更新,镜像源的具体地址可能会发生变化,因此,我将基于当前可获取的信息,提供一个通用的切换步骤,并附上最新的镜像地址(截至回答时)。一、了解npm镜像源npm(NodePackageManager)是JavaScript的包管理器,用于安装、更新和管理项目依赖。由于npm官方仓库</div> </li> <li><a href="/article/1835411044768509952.htm" title="字节二面" target="_blank">字节二面</a> <span class="text-muted">Redstone Monstrosity</span> <a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/%E9%9D%A2%E8%AF%95/1.htm">面试</a> <div>1.假设你是正在面试前端开发工程师的候选人,面试官让你详细说出你上一段实习过程的收获和感悟。在上一段实习过程中,我获得了宝贵的实践经验和深刻的行业洞察,以下是我的主要收获和感悟:一、专业技能提升框架应用熟练度:通过实际项目,我深入掌握了React、Vue等前端框架的使用,不仅提升了编码效率,还学会了如何根据项目需求选择合适的框架。问题解决能力:在实习期间,我遇到了许多预料之外的技术难题。通过查阅文</div> </li> <li><a href="/article/1835398064727224320.htm" title="前端代码上传文件" target="_blank">前端代码上传文件</a> <span class="text-muted">余生逆风飞翔</span> <a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a> <div>点击上传文件import{ElNotification}from'element-plus'import{API_CONFIG}from'../config/index.js'import{UploadFilled}from'@element-plus/icons-vue'import{reactive}from'vue'import{BASE_URL}from'../config/index'i</div> </li> <li><a href="/article/1835385458356482048.htm" title="uniapp实现动态标记效果详细步骤【前端开发】" target="_blank">uniapp实现动态标记效果详细步骤【前端开发】</a> <span class="text-muted">2401_85123349</span> <a class="tag" taget="_blank" href="/search/uni-app/1.htm">uni-app</a> <div>第二个点在于实现将已经被用户标记的内容在下一次获取后刷新它的状态为已标记。这是什么意思呢?比如说上面gif图中的这些人物对象,有一些已被该用户添加为关心,那么当用户下一次进入该页面时,这些已经被添加关心的对象需要以“红心”状态显现出来。这个点的难度还不算大,只需要在每一次获取后端的内容后对标记对象进行状态更新即可。II.动态标记效果实现思路和步骤首先,整体的思路是利用动态类名对不同的元素进行选择。</div> </li> <li><a href="/article/1835383919906746368.htm" title="高性能javascript--算法和流程控制" target="_blank">高性能javascript--算法和流程控制</a> <span class="text-muted">海淀萌狗</span> <div>-for,while和do-while性能相当-避免使用for-in循环,==除非遍历一个属性量未知的对象==es5:for-in遍历的对象便不局限于数组,还可以遍历对象。原因:for-in每次迭代操作会同时搜索实例或者原型属性,for-in循环的每次迭代都会产生更多开销,因此要比其他循环类型慢,一般速度为其他类型循环的1/7。因此,除非明确需要迭代一个属性数量未知的对象,否则应避免使用for-i</div> </li> <li><a href="/article/1835373236217540608.htm" title="360前端星计划-动画可以这么玩" target="_blank">360前端星计划-动画可以这么玩</a> <span class="text-muted">马小蜗</span> <div>动画的基本原理定时器改变对象的属性根据新的属性重新渲染动画functionupdate(context){//更新属性}constticker=newTicker();ticker.tick(update,context);动画的种类1、JavaScript动画操作DOMCanvas2、CSS动画transitionanimation3、SVG动画SMILJS动画的优缺点优点:灵活度、可控性、性能</div> </li> <li><a href="/article/1835368019430305792.htm" title="Vue + Express实现一个表单提交" target="_blank">Vue + Express实现一个表单提交</a> <span class="text-muted">九旬大爷的梦</span> <div>最近在折腾一个cms系统,用的vue+express,但是就一个表单提交就弄了好久,记录一下。环境:Node10+前端:Vue服务端:Express依赖包:vueexpressaxiosexpress-formidableelement-ui(可选)前言:axiosget请求参数是:paramsaxiospost请求参数是:dataexpressget接受参数是req.queryexpresspo</div> </li> <li><a href="/article/1835360244646113280.htm" title="JavaScript中秋快乐!" target="_blank">JavaScript中秋快乐!</a> <span class="text-muted">Q_w7742</span> <a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/ecmascript/1.htm">ecmascript</a> <div>我们来实现一个简单的祝福网页~主要的难度在于使用canvas绘图当点击canvas时候,跳出“中秋节快乐”字样,需要注册鼠标单击事件和计时器。首先定义主要函数:初始化当点击canvas之后转到onCanvasClick函数,绘图生成灯笼。functiononCanvasClick(){//事件处理函数context.clearRect(0,0,canvas1.width,canvas1.heigh</div> </li> <li><a href="/article/1835359727924637696.htm" title="Nginx从入门到实践(三)" target="_blank">Nginx从入门到实践(三)</a> <span class="text-muted">听你讲故事啊</span> <div>动静分离动静分离是将网站静态资源(JavaScript,CSS,img等文件)与后台应用分开部署,提高用户访问静态代码的速度,降低对后台应用访问。动静分离的一种做法是将静态资源部署在nginx上,后台项目部署到应用服务器上,根据一定规则静态资源的请求全部请求nginx服务器,达到动静分离的目标。rewrite规则Rewrite规则常见正则表达式Rewrite主要的功能就是实现URL的重写,Ngin</div> </li> <li><a href="/article/1835354700392787968.htm" title="Nginx的使用场景:构建高效、可扩展的Web架构" target="_blank">Nginx的使用场景:构建高效、可扩展的Web架构</a> <span class="text-muted">张某布响丸辣</span> <a class="tag" taget="_blank" href="/search/nginx/1.htm">nginx</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/%E6%9E%B6%E6%9E%84/1.htm">架构</a> <div>Nginx,作为当今最流行的Web服务器和反向代理软件之一,凭借其高性能、稳定性和灵活性,在众多Web项目中扮演着核心角色。无论是个人博客、中小型网站,还是大型企业级应用,Nginx都能提供强大的支持。本文将探讨Nginx的几个主要使用场景,帮助读者理解如何在实际项目中充分利用Nginx的优势。1.静态文件服务对于包含大量静态文件(如HTML、CSS、JavaScript、图片等)的网站,Ngin</div> </li> <li><a href="/article/1835354447627251712.htm" title="前端知识点" target="_blank">前端知识点</a> <span class="text-muted">ZhangTao_zata</span> <a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/css/1.htm">css</a> <div>下面是一个最基本的html代码body{font-family:Arial,sans-serif;margin:20px;}//JavaScriptfunctionthatdisplaysanalertwhencalledfunctionshowMessage(){alert("Hello!Youclickedthebutton.");}MyFirstHTMLPageWelcometoMyPage</div> </li> <li><a href="/article/1835352325032603648.htm" title="第三十一节:Vue路由:前端路由vs后端路由的了解" target="_blank">第三十一节:Vue路由:前端路由vs后端路由的了解</a> <span class="text-muted">曹老师</span> <div>1.认识前端路由和后端路由前端路由相对于后端路由而言的,在理解前端路由之前先对于路由有一个基本的了解路由:简而言之,就是把信息从原地址传输到目的地的活动对于我们来说路由就是:根据不同的url地址展示不同的页面内容1.1后端路由以前咱们接触比较多的后端路由,当改变url地址时,浏览器会向服务器发送请求,服务器根据这个url,返回不同的资源内容后端路由的特点就是前端每次跳转到不同url地址,都会重新访</div> </li> <li><a href="/article/1835350917352878080.htm" title="华雁智科前端面试题" target="_blank">华雁智科前端面试题</a> <span class="text-muted">因为奋斗超太帅啦</span> <a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF%E7%AC%94%E8%AF%95%E9%9D%A2%E8%AF%95%E9%97%AE%E9%A2%98%E6%95%B4%E7%90%86/1.htm">前端笔试面试问题整理</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/ecmascript/1.htm">ecmascript</a> <div>1.var变量的提升题目:vara=1functionfun(){console.log(b)varb=2}fun()console.log(a)正确输出结果:undefined、1答错了,给一个大嘴巴子,错误答案输出结果为:2,1此题主要考察var定义的变量,作用域提升的问题,相当于varaa=1functionfun(){varbconsole.log(b)b=2}fun()console.l</div> </li> <li><a href="/article/1835350535818014720.htm" title="如何建设数据中台(五)——数据汇集—打破企业数据孤岛" target="_blank">如何建设数据中台(五)——数据汇集—打破企业数据孤岛</a> <span class="text-muted">weixin_47088026</span> <a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95%E5%92%8C%E6%80%BB%E7%BB%93/1.htm">学习记录和总结</a><a class="tag" taget="_blank" href="/search/%E4%B8%AD%E5%8F%B0/1.htm">中台</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E4%B8%AD%E5%8F%B0/1.htm">数据中台</a><a class="tag" taget="_blank" href="/search/%E7%A8%8B%E5%BA%8F%E4%BA%BA%E7%94%9F/1.htm">程序人生</a><a class="tag" taget="_blank" href="/search/%E7%BB%8F%E9%AA%8C%E5%88%86%E4%BA%AB/1.htm">经验分享</a> <div>数据汇集——打破企业数据孤岛要构建企业级数据中台,第一步就是将企业内部各个业务系统的数据实现互通互联,打破数据孤岛,主要通过数据汇聚和交换来实现。企业采集的数据可以是线上采集、线下数据采集、互联网数据采集、内部数据采集等。线上数据采集主要载体分为互联网和移动互联网两种,对应有系统平台、网页、H5、小程序、App等,可以采用前端或后端埋点方式采集数据。线下数据采集主要是通过硬件来采集,例如:WiFi</div> </li> <li><a href="/article/1835343473629294592.htm" title="分布式锁和spring事务管理" target="_blank">分布式锁和spring事务管理</a> <span class="text-muted">暴躁的鱼</span> <a class="tag" taget="_blank" href="/search/%E9%94%81%E5%8F%8A%E4%BA%8B%E5%8A%A1/1.htm">锁及事务</a><a class="tag" taget="_blank" href="/search/%E5%88%86%E5%B8%83%E5%BC%8F/1.htm">分布式</a><a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a> <div>最近开发一个小程序遇到一个需求需要实现分布式事务管理业务需求用户在使用小程序的过程中可以查看景点,对景点地区或者城市标记是否想去,那么需要统计一个地点被标记的人数,以及记录某个用户对某个地点是否标记为想去,用两个表存储数据,一个地点表记录改地点被标记的次数,一个用户意向表记录某个用户对某个地点是否标记为想去。由于可能有多个用户同时标记一个地点,每个用户在前端点击想去按钮之后,后台接收到请求,从数据</div> </li> <li><a href="/article/1835340577596600320.htm" title="前端CSS面试常见题" target="_blank">前端CSS面试常见题</a> <span class="text-muted">剑亦未配妥</span> <a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/1.htm">前端面试</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/css/1.htm">css</a><a class="tag" taget="_blank" href="/search/%E9%9D%A2%E8%AF%95/1.htm">面试</a> <div>边界塌陷盒模型有两种:W3C盒模型和IE盒模型,区别在于宽度是否包含边框定义:同时给兄弟/父子盒模型设置上下边距,理论上边距值是两者之和,实际上不是注意:浮动和定位不会产生边界塌陷;只有块级元素垂直方向才会产生margin合并margin计算方案margin同为正负:取绝对值大的值一正一负:求和父子元素边界塌陷解决父元素可以通过调整padding处理;设置overflowhidden,触发BFC子</div> </li> <li><a href="/article/114.htm" title="戴尔笔记本win8系统改装win7系统" target="_blank">戴尔笔记本win8系统改装win7系统</a> <span class="text-muted">sophia天雪</span> <a class="tag" taget="_blank" href="/search/win7/1.htm">win7</a><a class="tag" taget="_blank" href="/search/%E6%88%B4%E5%B0%94/1.htm">戴尔</a><a class="tag" taget="_blank" href="/search/%E6%94%B9%E8%A3%85%E7%B3%BB%E7%BB%9F/1.htm">改装系统</a><a class="tag" taget="_blank" href="/search/win8/1.htm">win8</a> <div>戴尔win8 系统改装win7 系统详述 第一步:使用U盘制作虚拟光驱:         1)下载安装UltraISO:注册码可以在网上搜索。         2)启动UltraISO,点击“文件”—》“打开”按钮,打开已经准备好的ISO镜像文 </div> </li> <li><a href="/article/241.htm" title="BeanUtils.copyProperties使用笔记" target="_blank">BeanUtils.copyProperties使用笔记</a> <span class="text-muted">bylijinnan</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a> <div>BeanUtils.copyProperties VS PropertyUtils.copyProperties 两者最大的区别是: BeanUtils.copyProperties会进行类型转换,而PropertyUtils.copyProperties不会。 既然进行了类型转换,那BeanUtils.copyProperties的速度比不上PropertyUtils.copyProp</div> </li> <li><a href="/article/368.htm" title="MyEclipse中文乱码问题" target="_blank">MyEclipse中文乱码问题</a> <span class="text-muted">0624chenhong</span> <a class="tag" taget="_blank" href="/search/MyEclipse/1.htm">MyEclipse</a> <div>一、设置新建常见文件的默认编码格式,也就是文件保存的格式。 在不对MyEclipse进行设置的时候,默认保存文件的编码,一般跟简体中文操作系统(如windows2000,windowsXP)的编码一致,即GBK。 在简体中文系统下,ANSI 编码代表 GBK编码;在日文操作系统下,ANSI 编码代表 JIS 编码。 Window-->Preferences-->General -</div> </li> <li><a href="/article/495.htm" title="发送邮件" target="_blank">发送邮件</a> <span class="text-muted">不懂事的小屁孩</span> <a class="tag" taget="_blank" href="/search/send+email/1.htm">send email</a> <div> import org.apache.commons.mail.EmailAttachment; import org.apache.commons.mail.EmailException; import org.apache.commons.mail.HtmlEmail; import org.apache.commons.mail.MultiPartEmail; </div> </li> <li><a href="/article/622.htm" title="动画合集" target="_blank">动画合集</a> <span class="text-muted">换个号韩国红果果</span> <a class="tag" taget="_blank" href="/search/html/1.htm">html</a><a class="tag" taget="_blank" href="/search/css/1.htm">css</a> <div>动画 指一种样式变为另一种样式 keyframes应当始终定义0 100 过程 1 transition  制作鼠标滑过图片时的放大效果 css .wrap{ width: 340px;height: 340px; position: absolute; top: 30%; left: 20%; overflow: hidden; bor</div> </li> <li><a href="/article/749.htm" title="网络最常见的攻击方式竟然是SQL注入" target="_blank">网络最常见的攻击方式竟然是SQL注入</a> <span class="text-muted">蓝儿唯美</span> <a class="tag" taget="_blank" href="/search/sql%E6%B3%A8%E5%85%A5/1.htm">sql注入</a> <div>NTT研究表明,尽管SQL注入(SQLi)型攻击记录详尽且为人熟知,但目前网络应用程序仍然是SQLi攻击的重灾区。 信息安全和风险管理公司NTTCom Security发布的《2015全球智能威胁风险报告》表明,目前黑客攻击网络应用程序方式中最流行的,要数SQLi攻击。报告对去年发生的60亿攻击 行为进行分析,指出SQLi攻击是最常见的网络应用程序攻击方式。全球网络应用程序攻击中,SQLi攻击占</div> </li> <li><a href="/article/876.htm" title="java笔记2" target="_blank">java笔记2</a> <span class="text-muted">a-john</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a> <div>类的封装: 1,java中,对象就是一个封装体。封装是把对象的属性和服务结合成一个独立的的单位。并尽可能隐藏对象的内部细节(尤其是私有数据) 2,目的:使对象以外的部分不能随意存取对象的内部数据(如属性),从而使软件错误能够局部化,减少差错和排错的难度。 3,简单来说,“隐藏属性、方法或实现细节的过程”称为——封装。 4,封装的特性:       4.1设置</div> </li> <li><a href="/article/1003.htm" title="[Andengine]Error:can't creat bitmap form path “gfx/xxx.xxx”" target="_blank">[Andengine]Error:can't creat bitmap form path “gfx/xxx.xxx”</a> <span class="text-muted">aijuans</span> <a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0Android%E9%81%87%E5%88%B0%E7%9A%84%E9%94%99%E8%AF%AF/1.htm">学习Android遇到的错误</a> <div>        最开始遇到这个错误是很早以前了,以前也没注意,只当是一个不理解的bug,因为所有的texture,textureregion都没有问题,但是就是提示错误。 昨天和美工要图片,本来是要背景透明的png格式,可是她却给了我一个jpg的。说明了之后她说没法改,因为没有png这个保存选项。 我就看了一下,和她要了psd的文件,还好我有一点</div> </li> <li><a href="/article/1130.htm" title="自己写的一个繁体到简体的转换程序" target="_blank">自己写的一个繁体到简体的转换程序</a> <span class="text-muted">asialee</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E8%BD%AC%E6%8D%A2/1.htm">转换</a><a class="tag" taget="_blank" href="/search/%E7%B9%81%E4%BD%93/1.htm">繁体</a><a class="tag" taget="_blank" href="/search/filter/1.htm">filter</a><a class="tag" taget="_blank" href="/search/%E7%AE%80%E4%BD%93/1.htm">简体</a> <div>          今天调研一个任务,基于java的filter实现繁体到简体的转换,于是写了一个demo,给各位博友奉上,欢迎批评指正。          实现的思路是重载request的调取参数的几个方法,然后做下转换。           </div> </li> <li><a href="/article/1257.htm" title="android意图和意图监听器技术" target="_blank">android意图和意图监听器技术</a> <span class="text-muted">百合不是茶</span> <a class="tag" taget="_blank" href="/search/android/1.htm">android</a><a class="tag" taget="_blank" href="/search/%E6%98%BE%E7%A4%BA%E6%84%8F%E5%9B%BE/1.htm">显示意图</a><a class="tag" taget="_blank" href="/search/%E9%9A%90%E5%BC%8F%E6%84%8F%E5%9B%BE/1.htm">隐式意图</a><a class="tag" taget="_blank" href="/search/%E6%84%8F%E5%9B%BE%E7%9B%91%E5%90%AC%E5%99%A8/1.htm">意图监听器</a> <div>Intent是在activity之间传递数据;Intent的传递分为显示传递和隐式传递   显式意图:调用Intent.setComponent() 或 Intent.setClassName() 或 Intent.setClass()方法明确指定了组件名的Intent为显式意图,显式意图明确指定了Intent应该传递给哪个组件。   隐式意图;不指明调用的名称,根据设</div> </li> <li><a href="/article/1384.htm" title="spring3中新增的@value注解" target="_blank">spring3中新增的@value注解</a> <span class="text-muted">bijian1013</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/%40Value/1.htm">@Value</a> <div>        在spring 3.0中,可以通过使用@value,对一些如xxx.properties文件中的文件,进行键值对的注入,例子如下: 1.首先在applicationContext.xml中加入:  <beans xmlns="http://www.springframework.</div> </li> <li><a href="/article/1511.htm" title="Jboss启用CXF日志" target="_blank">Jboss启用CXF日志</a> <span class="text-muted">sunjing</span> <a class="tag" taget="_blank" href="/search/log/1.htm">log</a><a class="tag" taget="_blank" href="/search/jboss/1.htm">jboss</a><a class="tag" taget="_blank" href="/search/CXF/1.htm">CXF</a> <div>1. 在standalone.xml配置文件中添加system-properties:     <system-properties>        <property name="org.apache.cxf.logging.enabled" value=&</div> </li> <li><a href="/article/1638.htm" title="【Hadoop三】Centos7_x86_64部署Hadoop集群之编译Hadoop源代码" target="_blank">【Hadoop三】Centos7_x86_64部署Hadoop集群之编译Hadoop源代码</a> <span class="text-muted">bit1129</span> <a class="tag" taget="_blank" href="/search/centos/1.htm">centos</a> <div>  编译必需的软件 Firebugs3.0.0 Maven3.2.3 Ant JDK1.7.0_67 protobuf-2.5.0 Hadoop 2.5.2源码包       Firebugs3.0.0   http://sourceforge.jp/projects/sfnet_findbug</div> </li> <li><a href="/article/1765.htm" title="struts2验证框架的使用和扩展" target="_blank">struts2验证框架的使用和扩展</a> <span class="text-muted">白糖_</span> <a class="tag" taget="_blank" href="/search/%E6%A1%86%E6%9E%B6/1.htm">框架</a><a class="tag" taget="_blank" href="/search/xml/1.htm">xml</a><a class="tag" taget="_blank" href="/search/bean/1.htm">bean</a><a class="tag" taget="_blank" href="/search/struts/1.htm">struts</a><a class="tag" taget="_blank" href="/search/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F/1.htm">正则表达式</a> <div>struts2能够对前台提交的表单数据进行输入有效性校验,通常有两种方式: 1、在Action类中通过validatexx方法验证,这种方式很简单,在此不再赘述; 2、通过编写xx-validation.xml文件执行表单验证,当用户提交表单请求后,struts会优先执行xml文件,如果校验不通过是不会让请求访问指定action的。 本文介绍一下struts2通过xml文件进行校验的方法并说</div> </li> <li><a href="/article/1892.htm" title="记录-感悟" target="_blank">记录-感悟</a> <span class="text-muted">braveCS</span> <a class="tag" taget="_blank" href="/search/%E6%84%9F%E6%82%9F/1.htm">感悟</a> <div>再翻翻以前写的感悟,有时会发现自己很幼稚,也会让自己找回初心。   2015-1-11 1. 能在工作之余学习感兴趣的东西已经很幸福了; 2. 要改变自己,不能这样一直在原来区域,要突破安全区舒适区,才能提高自己,往好的方面发展; 3. 多反省多思考;要会用工具,而不是变成工具的奴隶; 4. 一天内集中一个定长时间段看最新资讯和偏流式博</div> </li> <li><a href="/article/2019.htm" title="编程之美-数组中最长递增子序列" target="_blank">编程之美-数组中最长递增子序列</a> <span class="text-muted">bylijinnan</span> <a class="tag" taget="_blank" href="/search/%E7%BC%96%E7%A8%8B%E4%B9%8B%E7%BE%8E/1.htm">编程之美</a> <div> import java.util.Arrays; import java.util.Random; public class LongestAccendingSubSequence { /** * 编程之美 数组中最长递增子序列 * 书上的解法容易理解 * 另一方法书上没有提到的是,可以将数组排序(由小到大)得到新的数组, * 然后求排序后的数组与原数</div> </li> <li><a href="/article/2146.htm" title="读书笔记5" target="_blank">读书笔记5</a> <span class="text-muted">chengxuyuancsdn</span> <a class="tag" taget="_blank" href="/search/%E9%87%8D%E5%A4%8D%E6%8F%90%E4%BA%A4/1.htm">重复提交</a><a class="tag" taget="_blank" href="/search/struts2%E7%9A%84token%E9%AA%8C%E8%AF%81/1.htm">struts2的token验证</a> <div>1、重复提交 2、struts2的token验证 3、用response返回xml时的注意 1、重复提交 (1)应用场景 (1-1)点击提交按钮两次。 (1-2)使用浏览器后退按钮重复之前的操作,导致重复提交表单。 (1-3)刷新页面 (1-4)使用浏览器历史记录重复提交表单。 (1-5)浏览器重复的 HTTP 请求。 (2)解决方法 (2-1)禁掉提交按钮 (2-2)</div> </li> <li><a href="/article/2273.htm" title="[时空与探索]全球联合进行第二次费城实验的可能性" target="_blank">[时空与探索]全球联合进行第二次费城实验的可能性</a> <span class="text-muted">comsci</span> <div>      二次世界大战前后,由爱因斯坦参加的一次在海军舰艇上进行的物理学实验 -费城实验   至今给我们大家留下很多迷团.....      关于费城实验的详细过程,大家可以在网络上搜索一下,我这里就不详细描述了      在这里,我的意思是,现在</div> </li> <li><a href="/article/2400.htm" title="easy connect 之 ORA-12154: TNS: 无法解析指定的连接标识符" target="_blank">easy connect 之 ORA-12154: TNS: 无法解析指定的连接标识符</a> <span class="text-muted">daizj</span> <a class="tag" taget="_blank" href="/search/oracle/1.htm">oracle</a><a class="tag" taget="_blank" href="/search/ORA-12154/1.htm">ORA-12154</a> <div>用easy connect连接出现“tns无法解析指定的连接标示符”的错误,如下: C:\Users\Administrator>sqlplus username/pwd@192.168.0.5:1521/orcl SQL*Plus: Release 10.2.0.1.0 – Production on 星期一 5月 21 18:16:20 2012 Copyright (c) 198</div> </li> <li><a href="/article/2527.htm" title="简单排序:归并排序" target="_blank">简单排序:归并排序</a> <span class="text-muted">dieslrae</span> <a class="tag" taget="_blank" href="/search/%E5%BD%92%E5%B9%B6%E6%8E%92%E5%BA%8F/1.htm">归并排序</a> <div> public void mergeSort(int[] array){ int temp = array.length/2; if(temp == 0){ return; } int[] a = new int[temp]; int</div> </li> <li><a href="/article/2654.htm" title="C语言中字符串的\0和空格" target="_blank">C语言中字符串的\0和空格</a> <span class="text-muted">dcj3sjt126com</span> <a class="tag" taget="_blank" href="/search/c/1.htm">c</a> <div>   \0 为字符串结束符,比如说:                       abcd (空格)cdefg; 存入数组时,空格作为一个字符占有一个字节的空间,我们</div> </li> <li><a href="/article/2781.htm" title="解决Composer国内速度慢的办法" target="_blank">解决Composer国内速度慢的办法</a> <span class="text-muted">dcj3sjt126com</span> <a class="tag" taget="_blank" href="/search/Composer/1.htm">Composer</a> <div>用法: 有两种方式启用本镜像服务: 1 将以下配置信息添加到 Composer 的配置文件 config.json 中(系统全局配置)。见“例1” 2 将以下配置信息添加到你的项目的 composer.json 文件中(针对单个项目配置)。见“例2” 为了避免安装包的时候都要执行两次查询,切记要添加禁用 packagist 的设置,如下 1 2 3 4 5 </div> </li> <li><a href="/article/2908.htm" title="高效可伸缩的结果缓存" target="_blank">高效可伸缩的结果缓存</a> <span class="text-muted">shuizhaosi888</span> <a class="tag" taget="_blank" href="/search/%E9%AB%98%E6%95%88%E5%8F%AF%E4%BC%B8%E7%BC%A9%E7%9A%84%E7%BB%93%E6%9E%9C%E7%BC%93%E5%AD%98/1.htm">高效可伸缩的结果缓存</a> <div>/** * 要执行的算法,返回结果v */ public interface Computable<A, V> { public V comput(final A arg); }   /** * 用于缓存数据 */ public class Memoizer<A, V> implements Computable<A, </div> </li> <li><a href="/article/3035.htm" title="三点定位的算法" target="_blank">三点定位的算法</a> <span class="text-muted">haoningabc</span> <a class="tag" taget="_blank" href="/search/c/1.htm">c</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a> <div>三点定位, 已知a,b,c三个顶点的x,y坐标 和三个点都z坐标的距离,la,lb,lc 求z点的坐标 原理就是围绕a,b,c 三个点画圆,三个圆焦点的部分就是所求 但是,由于三个点的距离可能不准,不一定会有结果, 所以是三个圆环的焦点,环的宽度开始为0,没有取到则加1 运行 gcc -lm test.c test.c代码如下 #include "stdi</div> </li> <li><a href="/article/3162.htm" title="epoll使用详解" target="_blank">epoll使用详解</a> <span class="text-muted">jimmee</span> <a class="tag" taget="_blank" href="/search/c/1.htm">c</a><a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a><a class="tag" taget="_blank" href="/search/%E6%9C%8D%E5%8A%A1%E7%AB%AF%E7%BC%96%E7%A8%8B/1.htm">服务端编程</a><a class="tag" taget="_blank" href="/search/epoll/1.htm">epoll</a> <div>epoll - I/O event notification facility在linux的网络编程中,很长的时间都在使用select来做事件触发。在linux新的内核中,有了一种替换它的机制,就是epoll。相比于select,epoll最大的好处在于它不会随着监听fd数目的增长而降低效率。因为在内核中的select实现中,它是采用轮询来处理的,轮询的fd数目越多,自然耗时越多。并且,在linu</div> </li> <li><a href="/article/3289.htm" title="Hibernate对Enum的映射的基本使用方法" target="_blank">Hibernate对Enum的映射的基本使用方法</a> <span class="text-muted">linzx0212</span> <a class="tag" taget="_blank" href="/search/enum/1.htm">enum</a><a class="tag" taget="_blank" href="/search/Hibernate/1.htm">Hibernate</a> <div>  枚举   /** * 性别枚举 */ public enum Gender { MALE(0), FEMALE(1), OTHER(2); private Gender(int i) { this.i = i; } private int i; public int getI</div> </li> <li><a href="/article/3416.htm" title="第10章 高级事件(下)" target="_blank">第10章 高级事件(下)</a> <span class="text-muted">onestopweb</span> <a class="tag" taget="_blank" href="/search/%E4%BA%8B%E4%BB%B6/1.htm">事件</a> <div>index.html <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/</div> </li> <li><a href="/article/3543.htm" title="孙子兵法" target="_blank">孙子兵法</a> <span class="text-muted">roadrunners</span> <a class="tag" taget="_blank" href="/search/%E5%AD%99%E5%AD%90/1.htm">孙子</a><a class="tag" taget="_blank" href="/search/%E5%85%B5%E6%B3%95/1.htm">兵法</a> <div>始计第一 孙子曰: 兵者,国之大事,死生之地,存亡之道,不可不察也。 故经之以五事,校之以计,而索其情:一曰道,二曰天,三曰地,四曰将,五 曰法。道者,令民于上同意,可与之死,可与之生,而不危也;天者,阴阳、寒暑 、时制也;地者,远近、险易、广狭、死生也;将者,智、信、仁、勇、严也;法 者,曲制、官道、主用也。凡此五者,将莫不闻,知之者胜,不知之者不胜。故校 之以计,而索其情,曰</div> </li> <li><a href="/article/3670.htm" title="MySQL双向复制" target="_blank">MySQL双向复制</a> <span class="text-muted">tomcat_oracle</span> <a class="tag" taget="_blank" href="/search/mysql/1.htm">mysql</a> <div>本文包括: 主机配置 从机配置 建立主-从复制 建立双向复制   背景 按照以下简单的步骤: 参考一下: 在机器A配置主机(192.168.1.30) 在机器B配置从机(192.168.1.29) 我们可以使用下面的步骤来实现这一点   步骤1:机器A设置主机 在主机中打开配置文件 , </div> </li> <li><a href="/article/3797.htm" title="zoj 3822 Domination(dp)" target="_blank">zoj 3822 Domination(dp)</a> <span class="text-muted">阿尔萨斯</span> <a class="tag" taget="_blank" href="/search/Mina/1.htm">Mina</a> <div> 题目链接:zoj 3822 Domination 题目大意:给定一个N∗M的棋盘,每次任选一个位置放置一枚棋子,直到每行每列上都至少有一枚棋子,问放置棋子个数的期望。 解题思路:大白书上概率那一张有一道类似的题目,但是因为时间比较久了,还是稍微想了一下。dp[i][j][k]表示i行j列上均有至少一枚棋子,并且消耗k步的概率(k≤i∗j),因为放置在i+1~n上等价与放在i+1行上,同理</div> </li> </ul> </div> </div> </div> <div> <div class="container"> <div class="indexes"> <strong>按字母分类:</strong> <a href="/tags/A/1.htm" target="_blank">A</a><a href="/tags/B/1.htm" target="_blank">B</a><a href="/tags/C/1.htm" target="_blank">C</a><a href="/tags/D/1.htm" target="_blank">D</a><a href="/tags/E/1.htm" target="_blank">E</a><a href="/tags/F/1.htm" target="_blank">F</a><a href="/tags/G/1.htm" target="_blank">G</a><a href="/tags/H/1.htm" target="_blank">H</a><a href="/tags/I/1.htm" target="_blank">I</a><a href="/tags/J/1.htm" target="_blank">J</a><a href="/tags/K/1.htm" target="_blank">K</a><a href="/tags/L/1.htm" target="_blank">L</a><a href="/tags/M/1.htm" target="_blank">M</a><a href="/tags/N/1.htm" target="_blank">N</a><a href="/tags/O/1.htm" target="_blank">O</a><a href="/tags/P/1.htm" target="_blank">P</a><a href="/tags/Q/1.htm" target="_blank">Q</a><a href="/tags/R/1.htm" target="_blank">R</a><a href="/tags/S/1.htm" target="_blank">S</a><a href="/tags/T/1.htm" target="_blank">T</a><a href="/tags/U/1.htm" target="_blank">U</a><a href="/tags/V/1.htm" target="_blank">V</a><a href="/tags/W/1.htm" target="_blank">W</a><a href="/tags/X/1.htm" target="_blank">X</a><a href="/tags/Y/1.htm" target="_blank">Y</a><a href="/tags/Z/1.htm" target="_blank">Z</a><a href="/tags/0/1.htm" target="_blank">其他</a> </div> </div> </div> <footer id="footer" class="mb30 mt30"> <div class="container"> <div class="footBglm"> <a target="_blank" href="/">首页</a> - <a target="_blank" href="/custom/about.htm">关于我们</a> - <a target="_blank" href="/search/Java/1.htm">站内搜索</a> - <a target="_blank" href="/sitemap.txt">Sitemap</a> - <a target="_blank" href="/custom/delete.htm">侵权投诉</a> </div> <div class="copyright">版权所有 IT知识库 CopyRight © 2000-2050 E-COM-NET.COM , All Rights Reserved. <!-- <a href="https://beian.miit.gov.cn/" rel="nofollow" target="_blank">京ICP备09083238号</a><br>--> </div> </div> </footer> <!-- 代码高亮 --> <script type="text/javascript" src="/static/syntaxhighlighter/scripts/shCore.js"></script> <script type="text/javascript" src="/static/syntaxhighlighter/scripts/shLegacy.js"></script> <script type="text/javascript" src="/static/syntaxhighlighter/scripts/shAutoloader.js"></script> <link type="text/css" rel="stylesheet" href="/static/syntaxhighlighter/styles/shCoreDefault.css"/> <script type="text/javascript" src="/static/syntaxhighlighter/src/my_start_1.js"></script> </body> </html><script data-cfasync="false" src="/cdn-cgi/scripts/5c5dd728/cloudflare-static/email-decode.min.js"></script>