Vue2.0 学习笔记

Vue概述与入门

  • Vue.js是目前最火的前端开发框架,React是最流行的前端开发框架(React除了开发网站还能开发手机APP,Vue也可用Weex进行手机APP开发)
  • Vue.js、Angular.js、React.js 并称前端三大主流框架
  • Vue.js 是一套构建用户界面的框架,只关注视图层,它不仅易于上手,还便于与第三方库或既有项目整合。
  • 前端的主要工作?主要负责MVC中的View层,主要与界面打交道。

为什么要学习流行框架

  • 企业为了提高开发效率:在企业中,时间就是效率,效率就是金钱。
    • 企业中,使用框架,能够大幅度提高开发效率。
  • 提高开发效率的发展历程:原生JS(兼容性) -> JQuery之类的类库(屏蔽兼容性,但是需要频繁操作DOM)->前端模板引擎(性能较低)->Angular.js / Vue.js (能够帮助我们减少不必要的DOM操作,提高渲染效率;双向数据绑定:【通过框架提供的指令,前端程序员只需要关心业务逻辑,不再关心DOM是如何渲染的了】)
  • 在Vue中,一个核心的概念,让用户不再操作DOM元素,让程序员有更多的时间关注业务逻辑的实现。
  • 增强就业竞争力(人无我有,人有我优)

框架和库的区别

  • 框架:是一套完整的技术解决方案;对项目侵入性较大,依赖性强,项目如果需要更换框架,则需要重新架构整个项目。
  • 库(插件):提供某一个小功能,对项目侵入性较小,如果库无法完成某些需求,可以很容易切换到其他实现。(例如:1.从JQuery切换到Zepto;2.从EJS切换到art-Template)

Node(后端)中的MVC与前端中的MVVM之间的区别

  • MVC是后端的分层开发概念:M指Model,主要是指数据模型的管理CRUD操作。V指View,指视图可以理解为前端。C指Controller层,指控制器层,主要包含将数据模型加工处理后传递到视图层渲染的控制器。
  • MVVM 是前端视图层的分层开发思想,主要把每个页面,分成了M、V和VM,其中,VM是MVVM的核心;因为VM是M和V之间的调度者;M指每个页面中单独的数据,V就是每个页面中的HTML结构,ViewModel它是一个调度者,分割了M和V。前端页面中使用MVVVM的思想名主要是为了让我们开发更加方便,因为MVVM提供了数据的双向绑定。数据的双向绑定是由VM提供的。
    • View层:
      • 视图层
      • 在前端开发中,通常就是DOM层
      • 主要的作用是给用户展示各种信息。
    • Model层:
      • 数据层
      • 数据可能是我们固定的死数据,更多的是来自我们服务器,从网路上请求下来的数据
    • VueModel层:
      • 视图模型层
      • 试图模型层是View 和 Model沟通的桥梁
      • 一方面实现了DataBinding,将Model的改变实时反应到View中
      • 另一方面实现了DOMListener,DOM监听,当DOM发生一些事件时,可以监听到,并在需要的情况下改变对应的Data

语法

声明Vue实例

  • 使用script导入vue.js,每个 Vue 应用都是通过用 Vue 函数创建一个新的 Vue 实例开始的:
<script src="../js/vue.js"></script>
<script>
        const app = new Vue({
            el: '#app',
            data: {
                movies: ['星际穿越', '大话西游', '少年派', '盗梦空间']
            }
        })
    </script>
  • 创建Vue实例时,我们向构造函数中传递了一个对象,对象包含以下属性:
    • el: 待绑定的Element元素
      • 类型:String | HTMLElement
      • 作用: 决定Vue实例会管理哪个BOM
    • data: 声明数据对象(Model)
      • 类型: Object | Function (组件中data必须是一个函数)
      • 作用: Vue实例对应的数据对象,常用this获取
    • methods: 自定义Vue实例的方法
      • 类型: {[key: string]: Function}
      • 作用: 定义属于Vue的一些方法,可以在其他地方调用,也可以在指令中使用
  • 当一个 Vue 实例被创建时,它将 data 对象中的所有的 property 加入到 Vue 的响应式系统中。当这些 property 的值发生改变时,视图将会产生“响应”,即匹配更新为新的值。当这些数据改变时,视图会进行重渲染。值得注意的是只有当实例被创建时就已经存在于 data 中的 property 才是响应式的。如果你知道你会在晚些时候需要一个property,但是一开始它为空或不存在,那么你仅需要设置一些初始值。
  • 使用 Object.freeze(),这会阻止修改现有的 property,也意味着响应系统无法再追踪变化。
  • 除了数据 property,Vue 实例还暴露了一些有用的实例 property 与方法。它们都有前缀 $,以便与用户定义的 property 区分开来。

生命周期

每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做生命周期钩子(hook)的函数,这给了用户在不同阶段添加自己的代码的机会。
Vue生命周期
比如 created 钩子可以用来在一个实例被创建之后执行代码:

new Vue({
  data: {
    a: 1
  },
  created: function () {
  
    // `this` 指向 vm 实例
    console.log('a is: ' + this.a)
  }
})
// => "a is: 1"

也有一些其它的钩子,在实例生命周期的不同阶段被调用,如 mounted\updated\destroyed。生命周期钩子的 this 上下文指向调用它的 Vue 实例。

插值操作

mustache(胡须)语法

通过使用双大括号包裹data中的key的方式,插入Vue实例绑定的数据。

<span>Message: {{ msg }}</span>

Mustache 标签将会被替代为对应数据对象上 msg property 的值。无论何时,绑定的数据对象上 msg property 发生了改变,插值处的内容都会更新。

v-once指令

只加载一次data中的数据,后面更改不会改变该值,无需赋值。
执行一次性地插值,当数据改变时,插值处的内容不会更新。但请留心这会影响到该节点上的其它数据绑定:

<span v-once>这个将不会改变: {{ msg }}</span>

v-html

以html格式展示和解析数据

<p>Using mustaches: {{ rawHtml }}</p>
<p>Using v-html directive: <span v-html="rawHtml"></span></p>

这个 span 的内容将会被替换成为 property 值 rawHtml,直接作为 HTML——会忽略解析 property 值中的数据绑定。注意,你不能使用 v-html 来复合局部模板,因为 Vue 不是基于字符串的模板引擎。反之,对于用户界面 (UI),组件更适合作为可重用和可组合的基本单位。

你的站点上动态渲染的任意 HTML 可能会非常危险,因为它很容易导致 XSS 攻击。请只对可信内容使用 HTML 插值,绝不要对用户提供的内容使用插值。

v-pre

原封不动显示内容

v-cloak(斗篷)

在解析之前存在,通过修改该属性的display样式达到去除插值闪烁问题,因为解析完成后Vue会自动移除该属性。

数据绑定

前面学习的是将值插入到模板内容中,而数据绑定是指将数据动态绑定给元素的属性。例如:a元素中的href,img元素中的src

v-bind

Mustache 语法不能作用在 HTML attribute 上,遇到这种情况应该使用v-bind 指令

<div v-bind:id="dynamicId"></div>

对于布尔 attribute (它们只要存在就意味着值为 true),v-bind 工作起来略有不同

<button v-bind:disabled="isButtonDisabled">Button</button>

如果 isButtonDisabled 的值是 nullundefinedfalse,则 disabled attribute 甚至不会被包含在渲染出来的 <button> 元素中。

  • v-bind 动态绑定class和style

    • v-bind绑定style时,支持驼峰和原style名两种,例如fontSizefont-size
      • 赋值时需要将样式值变为字符串否则会作为对象进行解析从而报错
    • 对象语法
      <h2 v-bind:class="{类名1:true,类名2:boolean}{{message}}</h2>"
      
      class中的值为固定样式,v-bind:class中的为动态样式。
    • 数组语法
      <h2 class="title" :class="[active, line]">
      
  • v-bind 语法糖
    v-bind: 可以简写为 :

计算属性

vue实例中添加计算属性:computed
模板尽量简单易懂,对于任何复杂逻辑,你都应当使用计算属性。

<div id="example">
  <p>Original message: "{{ message }}"</p>
  <p>Computed reversed message: "{{ reversedMessage }}"</p>
</div>
var vm = new Vue({
  el: '#example',
  data: {
    message: 'Hello'
  },
  computed: {
    // 计算属性的 getter
    reversedMessage: function () {
      // `this` 指向 vm 实例
      return this.message.split('').reverse().join('')
    }
  }
})

这里声明了一个计算属性 reversedMessage。Vue提供的函数将用作 property vm.reversedMessage 的 getter 函数:

console.log(vm.reversedMessage) // => 'olleH'
vm.message = 'Goodbye'
console.log(vm.reversedMessage) // => 'eybdooG'
计算属性缓存 vs 方法

你可能已经注意到我们可以通过在表达式中调用方法来达到同样的效果:

<p>Reversed message: "{{ reversedMessage() }}"</p>
// 在组件中
methods: {
  reversedMessage: function () {
    return this.message.split('').reverse().join('')
  }
}

我们可以将同一函数定义为一个方法而不是一个计算属性。两种方式的最终结果确实是完全相同的。然而,不同的是计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。这就意味着只要 message 还没有发生改变,多次访问 reversedMessage 计算属性会立即返回之前的计算结果,而不必再次执行函数。

这也同样意味着下面的计算属性将不再更新,因为 Date.now() 不是响应式依赖:

computed: {
  now: function () {
    return Date.now()
  }
}

相比之下,每当触发重新渲染时,调用方法将总会再次执行函数。

我们为什么需要缓存?假设我们有一个性能开销比较大的计算属性 A,它需要遍历一个巨大的数组并做大量的计算。然后我们可能有其他的计算属性依赖于 A。如果没有缓存,我们将不可避免的多次执行 A 的 getter!如果你不希望有缓存,请用方法来替代。

计算属性 vs 侦听属性

Vue 提供了一种更通用的方式来观察和响应 Vue 实例上的数据变动:侦听属性。当你有一些数据需要随着其它数据变动而变动时,你很容易滥用 watch——特别是如果你之前使用过 AngularJS。然而,通常更好的做法是使用计算属性而不是命令式的 watch 回调。细想一下这个例子:

<div id="demo">{{ fullName }}</div>
var vm = new Vue({
  el: '#demo',
  data: {
    firstName: 'Foo',
    lastName: 'Bar',
    fullName: 'Foo Bar'
  },
  watch: {
    firstName: function (val) {
      this.fullName = val + ' ' + this.lastName
    },
    lastName: function (val) {
      this.fullName = this.firstName + ' ' + val
    }
  }
})

上面代码是命令式且重复的。将它与计算属性的版本进行比较:

var vm = new Vue({
  el: '#demo',
  data: {
    firstName: 'Foo',
    lastName: 'Bar'
  },
  computed: {
    fullName: function () {
      return this.firstName + ' ' + this.lastName
    }
  }
})

好得多了,不是吗?

计算属性的 setter

计算属性默认只有 getter,不过在需要时你也可以提供一个 setter:

// ...
computed: {
  fullName: {
    // getter
    get: function () {
      return this.firstName + ' ' + this.lastName
    },
    // setter
    set: function (newValue) {
      var names = newValue.split(' ')
      this.firstName = names[0]
      this.lastName = names[names.length - 1]
    }
  }
}
// ...

现在再运行 vm.fullName = 'John Doe' 时,setter 会被调用,vm.firstNamevm.lastName 也会相应地被更新。

侦听器

虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的侦听器。这就是为什么 Vue 通过 watch 选项提供了一个更通用的方法,来响应数据的变化。当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。

例如:

<div id="watch-example">
  <p>
    Ask a yes/no question:
    <input v-model="question">
  </p>
  <p>{{ answer }}</p>
</div>
<!-- 因为 AJAX 库和通用工具的生态已经相当丰富,Vue 核心代码没有重复 -->
<!-- 提供这些功能以保持精简。这也可以让你自由选择自己更熟悉的工具。 -->
<script src="https://cdn.jsdelivr.net/npm/axios@0.12.0/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lodash@4.13.1/lodash.min.js"></script>
<script>
var watchExampleVM = new Vue({
  el: '#watch-example',
  data: {
    question: '',
    answer: 'I cannot give you an answer until you ask a question!'
  },
  watch: {
    // 如果 `question` 发生改变,这个函数就会运行
    question: function (newQuestion, oldQuestion) {
      this.answer = 'Waiting for you to stop typing...'
      this.debouncedGetAnswer()
    }
  },
  created: function () {
    // `_.debounce` 是一个通过 Lodash 限制操作频率的函数。
    // 在这个例子中,我们希望限制访问 yesno.wtf/api 的频率
    // AJAX 请求直到用户输入完毕才会发出。想要了解更多关于
    // `_.debounce` 函数 (及其近亲 `_.throttle`) 的知识,
    // 请参考:https://lodash.com/docs#debounce
    this.debouncedGetAnswer = _.debounce(this.getAnswer, 500)
  },
  methods: {
    getAnswer: function () {
      if (this.question.indexOf('?') === -1) {
        this.answer = 'Questions usually contain a question mark. ;-)'
        return
      }
      this.answer = 'Thinking...'
      var vm = this
      axios.get('https://yesno.wtf/api')
        .then(function (response) {
          vm.answer = _.capitalize(response.data.answer)
        })
        .catch(function (error) {
          vm.answer = 'Error! Could not reach the API. ' + error
        })
    }
  }
})
</script>

在这个示例中,使用 watch 选项允许我们执行异步操作 (访问一个 API),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。

除了 watch 选项之外,您还可以使用命令式的 vm.$watch API

属性的getter和setter方法

Vue的属性都有getter和setter方法

  • 计算属性一般没有set方法,只读属性,其实可以使用set方法,传入新值作为参数。
...
computed:{
    fullName:function () {
        ...
    }
    
    等价于
    
    fullName:{ 
        get: function () {
        ...
        }
    }
}
...

事件监听

在前端开发中,需要经常和用户的行为事件进行交互,在这时就必须监听用户行为发生的事件,在Vue中主要使用v-on指令进行事件监听

v-on

  • 作用:绑定事件监听器
  • 简写: @
  • 预期: Function | Inline Statement | Object
  • 参数:event

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

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

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

  2. 情况二:若需要传递多个参数,需要手动获取浏览器的event对象:$event
  • stop修饰符的使用
    阻止事件冒泡,可以为@事件加上修饰符@事件.stop

  • .prevent修饰符的使用
    阻止网页元素的事件默认行为,例如form表单自动提交

  • .{keyCode | keyAlias}修饰符
    只当事件时从特定键触发时才触发回调。

  • .native 监听组件根元素的原生事件(自定义组件)

  • .once 只触发一次回调

条件判断

v-if、v-else-if、v-else

  • 这三个指令与JavaScript的条件语句if、else、else if类似。
  • Vue的条件指令可以根据表达式的值在DOM中渲染或销毁元素或组件
  • 存在的小问题:当两个input标签被v-if和v-else分别展示后,第一个input的内容会复用到第二个input中。
  • 问题原因:这是由于Vue在进行DOM渲染时,出于性能考虑,会尽可能的复用已经存在的元素,而不是重新创建新性元素。
  • 解决办法:如果不希望出现类似的复用问题,可以给相应的元素添加key属性,key值不同。

v-show 和 v-if对比

  • 当v-if的条件语句为false时,包含v-if指令的元素,根本不会在dom中存在。
  • 当条件为false时,v-show只是给元素添加一个行内样式:display: none
  • 当需要在显示与隐藏切换非常频繁,需要v-show
  • 只切换一次使用v-if。

循环语句

v-for

  1. 在遍历过程中,没有使用索引值
<ul>
    <li v-for="item in names">{{item}}</li>
</ul>
  1. 在遍历过程中,获取索引值
<ul>
    <li v-for="(item, index) in names">
    {{index+1}}.{{item}}
    </li>
</ul>
遍历对象
  • 在遍历对象的过程中,如果只获取一个值,那么获取到的是Value
  • 获取key和Value 格式:(value,key)
  • 获取key、value和index 格式: (value, key, index)

官方推荐我们在使用v-for时,给对应的元素或组件上添加上一个key属性。目的时为了更好的复用。注意需要唯一性。

  • 哪些数组的方法是响应式的
    • .push()
    • .pop() 删除最后一个
    • .shift() 删除第一个
    • .unshift() 在数组最前面添加元素
    • .splice() 删除/插入/替换 元素 第二个参数传入你要删除几个元素;第二个参数,标识要替换几个元素,后面参数用于替换的元素;第二个参数传入0,并且后面参数为要插入的参数
    • .sort()
    • .reverse()

传递多个参数,可以用可变参数...num

  • 哪些数组的方法不是响应式的
    • 通过索引值修改数组中的元素
    • 若需要替换数组元素,可以用splice函数或者Vue.set
    • Vue.set(要修改的对象,索引值,修改后的值)

函数式编程:高阶函数:filter/map/reduce

  1. filter函数
    filter中的回调函数有一个要求,必须要返回一个布尔值,true:函数内部会自动将这次回调的n加入到新的数组中,false函数会过滤掉这次的n
  2. map函数(映射函数)
    传入回调函数,返回回调函数的返回值的集合
  3. reduce函数
    数组中所有的内容进行汇总
    返回值需要时数字,给定初始值,通过遍历数组,将前一个值(第一次即为初始值)和遍历的值相加得到结果赋值给下一次循环的PreValue,直至完成遍历

表单绑定v-model

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

  • input中的内容与Vue中的变量进行双向绑定,当一方修改,另一方跟着同步。
  • v-model:text
  • v-model:radio
  • v-model:checkbox 用数组保存
  • v-model:select 单选,绑定到select元素上 多选加上 multiple
值绑定
<label v-for="item in originHobbies" :for="item">
    <inout type="checkbox" :value="item" :id="item" v-model="hobbies">{{item}}
</label>
修饰符
  • .lazy 当用户敲击回车或者该元素失去焦点时同步数据到data变量
  • .number 限制元素的内容数据类型
  • .trim

组件化

将一个页面中的所有处理逻辑全部放在一起,处理起来会变得非常复杂,而且不利于后续管理和扩展,但是如果将每个页面拆分成小的功能块,每个功能块能完成属于自己这部分独立的功能,之后整个页面的管理和维护就变得非常容易。

  • 组件话是Vue.js中的重要思想
    • 它提供了一种抽象,让我们可以开发出一个个独立可复用的小组件来构造我们的应用
    • 仍和应用都会被抽成一颗组件树

封装组件

注册组件的基本步骤
  • 组件的使用分成3个步骤:
    • 创建组件构造器 调用Vue.extend()方法
      • 调用Vue.extend()创建的是一个组件构造器。
      • 通常在创建组件构造器时,传入template代表我们自定义组件的模板。
      • 该模板就是在使用到组件的地方,要显示的HTML代码
      • 事实上在Vue2.X的文档中几乎已经看不到了,它会直接使用语法糖,这种方式时后面方式的基础。
    • 注册组件 Vue.cpmponent()方法
      • 调用Vue.component()时将刚才的组件构造器注册为一个组件,并给它取一个组件的标签名称。
      • 所以需要传递两个参数:1.注册组件的标签名,2.组件构造器
    • 使用组件 在Vue实例的作用范围内使用组件
      • 必须挂载在Vue实例下

ES6中可以使用``声明多行字符串(模板字符串)

全局组件和局部组件
  • 全局组件:意味着可以在多个Vue的实例下面使用的
  • 局部组件:在vue实例中添加component属性
父组件与子组件
  • 如何形成父子组件的关系?
    • 在组件构造器声明时,加速另一个属性components,可以在该构造器的模板中使用另外的组件,该构造器声明的组件就是父组件,在器中引用的组件称为子组件
    • root组件-vue实例
    • 组件和组件之间存在层级关系
    • 而其中一种非常重要的关系就是父子组件的关系
  • 父子组件错误用法:以子标签的形式在Vue实例中使用
    • 因为当子组件注册到父组件的components时,Vue会编译好父组件的模块
    • 该模板的内容已经决定了父组件酱油渲染的HTML(相当于父组件中已经有利子组件中的内容了)
    • <child-cpn></child-cpn>是只能在父组件中被识别的。否则会被浏览器忽略
组件的语法糖注册方式
  • 将原本传递给extend方法的模板内容,直接传递给component注册方法,Vue会底层自动调用extend方法
  • 局部语法糖同理,将原本传递给components属性一个组件构造器,现在可以直接将模板传递给components属性。
简易组件模板分离写法
  • 使用script标签,将标签的类型设置为text/x-template,并添加id,然后将id传递给组件构造函数的template中,template:'#cpn'
  • 使用template标签,并设置id
  • 组件中的标签只能有一个根元素。
组件可以访问Vue实例数据吗

注册组件时可以传入data函数,用于返回data对象实例。

  • 组件对象也有一个data属性(也可以有methods等属性)
  • 只是这个data属性必须是一个函数。
  • 而且这个函数单号一个对象,对象内部保存着数据。
为什么data是一个函数
  • 因为组件是用来复用的,且 JS 里对象是引用关系,如果组件中 data 是一个对象,那么这样作用域没有隔离,子组件中的 data 属性值会相互影响,
  • 如果组件中 data 选项是一个函数,那么每个实例可以维护一份被返回对象的独立的拷贝,组件实例之间的 data 属性值不会互相影响;
  • 而 new Vue 的实例,是不会被复用的,因此不存在引用对象的问题。
父子组件的通信
  • 通过props向子组件传递数据
    • 使用子组件props声明属性,然后通过v-bind语法将属性名和父组件的数据对象进行绑定,从而获取数据。
    • 可以传递对象,并将属性也设置为对象,在其中定义属性数据类型、默认值和是否必传
    • 属性的类型是对象或者数组时,默认值必须是一个函数 default() {return []}
    • 支持的数据类型:String、Number、Boolean、Array、Object、Date、Function、Symbol、自定义类型
    • 支持自定义验证函数validator
  • 通过自定义事件向父组件发送消息 $emit
    • 在子组件中的methods属性定义相关事件方法,并通过this.#emit将结果传递给父组件
父子组件直接访问
  • 父组件访问子组件:使用children或refs
    • this.$children是一个数组类型,它包含所有子组件对象
    • 通过遍历,可以去除所有子组件的message状态
    • $children通过数组访问,非常不方便,只有需要获取所有子组件才采用
    • 建议使用ref,可以为特定子组件添加ref属性,并通过`ref.[ref属性值]`,默认空对象
  • 子组件访问父组件: 使用$parent
    • 访问父组件$parent
    • 访问根组件$root

插槽slot

  • 插槽的目的是让组件具备更多扩展性

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

  • 根据外界传递的参数而显示内部展示

  • 最好的封装方式是将共性抽取到组件中,将不同暴露为插槽,预留插槽后就可以让使用者根据自己的需求来决定插入插槽的内容。

  • 在模板需要开发自定义的部分使用标签定义插槽,用户可以从外部传入指定元素到该位置进行展示或操作,可以定义默认值,意味着当用户不传递自定义内容即使用默认值,传递即覆盖默认值。

  • 具名插槽

  • 在slot标签中添加name属性,再在自定义的最外层标签添加slot属性并把name属性值赋值给slot就可以针对性替换。

编译作用域
父组件模板的所有东西都会在父级作用域内编译;子组件模板的所有东西都会在子级作用域内编译;

  • 作用域插槽:准备
  • 父组件替换插槽的标签,但是内容由子组件来提供
  • 使用slot-scope获取子组件中插槽对象 然后再使用slot.属性名获取子组件中定义的数据属性(声明方式:xxx,例如:data)
  • 作用,替换子组件插槽中对其数据内容的展示方式

模块化开发

  • 常见模块化规范:CommonJs、AMD、CMD、ES6 Modules
  • export(导出)/import(导入)
let name = 'why'
let age = 18
let height = 1.88
export {
    name,
    age
    height
}
  • 可以导出函数/类
import {mul, Person} from './aaa.js';
  • export default 默认只能由一个导出对象\
    • 一个模块中包含某个功能,不希望给这个功能命名,而且让导入者自行命名。
// info.js
export default function () {
    console.log('default function')
}
  • 统一导入
import * as xxx from 'xx/xxx.js'

WebPack

  • Webpack是一个现代的JavaScript应用的静态模块打包工具
  • 依赖于node环境和npm工具
  • 将npm包保存到 开发环境 --save -dev
  • 配置webpack配置文件,即可直接使用webpack文件

loader

  • loader是webpack中一个非常核心的概念
  • loader主要是将js、css、图片、ES6->ES5,TS->ES5,scss\less->css,.jsx、.vue文件转成.js文件
  • 使用步骤
    • 步骤一:通过npm安装需要使用的loader
    • 步骤二:webpack.config.js中的modules关键字下进行配置
    • css-loader只负责将css文件进行加载
    • style-loader负责将样式添加到DOM中
    • less-loader less预处理
    • url-loader 使用url加载的文件
      • option :limit 小于limit会将图片编译为base64字符串形式。超过了则需要使用file-loader
    • babel-loader 将ES6语法转换为ES5,提高代码浏览器兼容性
    • vue-loader、vue-template-compiler
    • 使用多个loader时,是从右向左

在module中的rule使用exclude排除,include包含
扩展名省略可以在resolve属性中,加入extension数组,数组中的值是需要省略的后缀名。

webpack配置vue

在webpack.config.json中为配置对象添加resolve属性,包含alias别名

resolve: {
    alias: {
        'vue$': 'vue/dist/vue.esm.js'
    }
}

插件 plugin

  • plugin通常用于对现有的架构进行扩展
  • webpack中的插件就是对webpack现有功能的各种扩展,比如打包优化,文件压缩。
  • loader和plugin的区别
    • loader主要用于转换某些类型的模块,它是一个转换器
    • plugin是插件,它是对webpack本身的扩展,是一个扩展器
  • 使用步骤:
    • 一、提高npm安装需要使用的plugins
    • 二、在webpack.config.js中的plugins中配置插件
  • 常用插件:
    • 添加版权 BannerPlugin
    • 将index.html打包到dist文件夹中,使用HtmlWebpackPlugin插件
      • 自动生产一个index.html文件(可以指定模板来生成)
      • 将打包的js文件,自动通过script标签插入到body中
    • 压缩js:uglifyjs-webpack-plugin
    • 搭建本地服务器: devserver
      • npm install --save -dev webpack-dev-server@2.9.1
      • contentBase:为哪一个文件夹提供本地服务,默认是根目录
      • port: 端口号
      • inline : 页面实时刷新
      • historyApiFallback:在SPA页面中,依赖HTML5的history模式。
      • webpack-merge 用于合并配置文件

Vue-CLI

  • 在开发大型项目时,必然需要使用Vue-CLI
    • 使用Vue.js开发大型应用,需要考虑代码目录结构、项目结构和部署、热加载、代码单元测试等事情。
    • 以上工作通常使用一些脚手架工具完成。
  • CLI是什么?
    • CLI是Command-Line Interface,翻译为命令行界面,俗称脚手架
    • Vue CLI是一个官方发布Vue.js项目的脚手架
    • 使用vue-cli可以快速搭建Vue开发环境以及对应的webpack配置
  • 安装Vue脚手架
    • npm install -g @vue/cli

Vue-Router 前端路由

路由->路由器,路由器的工作机制

  • 后端路由:后端处理URL和页面之间的映射关系
  • 前端渲染:浏览器中显示的网页中的大部分内容,都是由前端写的js代码在浏览器中执行,最终渲染出来的网页
  • 单页面富应用:spa主要特定是在前后端分离的基础上加了一层前端路由
  • 前端路由:操作页面上的内容后会生成特定url用于特定JS渲染页面,前端管理路由用于显示不同页面

如何改变URL

URL的hash
- url的hash也就是锚点(#),本质上是改变window.location的href属性
- 我们可以通过直接赋值location.hash来改变href,但是页面不发生刷新
history
- history.replaceState({},'', '/foo')
- history.pushState({},'', '')
- history.back
- history.go(-1)
- history.forword

vue-router的安装和使用

- 安装vue-router
    - `npm install vue-router --save`
- 在模块化工程中使用它
    - 1.导入路由对象,并且调用Vue.use(VueRouter)
    - 2.创建路由实例,并且传入路由映射配置
    - 3.在Vue实例中挂载创建的路由实例
- 使用vue-router的步骤:
    - 第一步:创建路由组件
    - 第二步:配置路由映射:组件和路径映射关系
    - 第三步:使用路由:通过<router-link>和<router-view>
    - 默认情况下,第一次访问网页希望展示首页的内容,只需要在映射关系中多配置一个默认映射即可,path配置的是根路径:/,redirect是重定向,也就是将路径重定向到/home的路径下
- router-link其他属性:
    - tag:tag可以指定<router-link>之后渲染成什么组件
    - replace:replace不会留下history记录,所以指定replace
    - active-class="active" 修改active类名
    - 在映射表中vue-router实例处添加属性:linkActiveClass:"自定义激活类名"
- 通过代码修改router
    - `this.$router.push('/home')` / `this.$router.replace('/about')`
- 通过代码统一修改active-class属性类名

动态路由

- path和Component的匹配,称为动态路由
- 示例:`/componentName/:data`  使用v-bind指令可以动态绑定url的数据内容
- 获取data值,`$route` 拿到活跃的路由 `$route.params.data`

路由的懒加载

- 当打包时,会将js分包,项目代码+底层支持代码+第三方依赖代码
- 用到时再加载称为懒加载,若我们能把不同路由对应的组件分割成不同的代码,然后当路由被访问的时候才加载对应组件。
- 将原来先导入再使用组件的方式改为在声明路由时直接使用箭头函数导入使用
const routes = [
    {
        path:'/home',
        component: () => import('../components.Home')
    },
    {
        path:'/about',
        component: () => import('../components.About')
    },
]

嵌套路由

- 当路由里需要再嵌套子路由时,可以给对应路由声明children里面的语法和声明路由一致,然后再在组件中添加对应组件渲染占位符。
const routes = [
    {
        path:'/home',
        component: () => import('../components.Home'),
        children: [
            {
                path:'/news',
                component: () => import('../components.News') 
            },
            {
                path:'/message',
                component: () => import('../components.Message') 
            }
            
        ]
    },
    {
        path:'/about',
        component: () => import('../components.About')
    },
]

不同组件(路由)传递参数的方式

- 主要有两种:params和query
- params
    - 配置路由格式:`/router/:id`
    - 传递的方式:在path后面跟上对应的值
    - 传递后形成的路径:`/router/123`、`/router/abc`
- query
    - 配置路由格式:/router,也就是普通配置
    - 传递方式: 对象中使用query的key作为传递方式
    - 传递后形成的路径:`/router?id=123`,`router?id=abc`

URL组成:协议://主机(域名):端口(80)/路径(path)?查询#片段(hash值)

  • 区分route和router
    • 所有的组件都继承自Vue的原型

导航守卫

  • created()、mounted()、updated() 生命周期
  • 全局导航守卫
// 跳转前置钩子
router.beforeEach((to,from,next)=>{
    //从 from 跳转到 to
    document.title = to.matched[0].meta.title
    next()必须调用
    
})

router.afterEach((to, from) => {
    console.log('----')
})

  • 路由独享守卫
    • 在声明路由数组时,为某一个路由配置守卫的属性和对应的值
  • 组件内的守卫
    • 在组件声明时,为组件添加守卫:beforeRouterEnter(to,from,next),beforeRouterUpdate(to,from,next),beforeRouterLeave(to,from,next)
    • 可以用于保存跳转时上一组件中的某些状态

keep-alive

- 使被包含的组件保留状态,避免重复渲染
- include 字符传或正则表达式,只有匹配的组件会被缓存
- exclude 字符串或正则表达式,仍会匹配的组件都不会被缓存
- router-view也是一个组件,如果直接被包在keep-alive里面,所有路径匹配到的视图组件都会被缓存
- 该标签包裹的内容,有两个钩子函数生效,activated和deactivated。

Promise

Promise是异步编程的解决方案。
常用场景:

  • 网络请求
  • 一般情况下是有异步操作,使用promise对这个异步操作进行封装
//resolve(成功调用), reject(失败调用) 本身又是函数
// 链式编程
new Promise((resolve, reject) =>{
    //第一次网络请求的代码
    网络请求操作(){
        resolve()
    }.then(() => {
        //第一次网络请求处理的代码
        .
        .
        .
        //第二次网络请求的代码
        return new Promise((resolve, reject) =>{ 
            网络请求操作(){
            resolve()
            }.then(() => {
                //第二次网络请求处理的代码
                .
                .
                .
                return new Promise((resolve, reject) =>{ ... }
            }
        })
    }
    
})
  • new->构造函数(1.保存了一些状态信息, 2.执行传入的回调函数)
  • 三种状态:
    • pending:等待状态。比如正在进行网络请求,或者定时器事件没有到
    • fulfill:满足状态,当我们主动回调了resolve时,就处于该状态,并且会会到.then()
    • reject:拒绝状态,当我们主动回调了reject时,就处于该状态,并且会回调.catch()
      async异步操作->weopped
//resolve(成功调用), reject(失败调用) 本身又是函数
// 链式编程
new Promise((resolve, reject) =>{
    //第一次网络请求的代码
    网络请求操作(){
        resolve(res , data)
    }.then((res) => {
        //第一次网络请求处理的代码
        .
        .
        .
        //第二次网络请求的代码
        return new Promise((resolve, reject) =>{ 
            网络请求操作(){
            resolve(res , data)
            }.then((res) => {
                //第二次网络请求处理的代码
                .
                .
                .
                return new Promise((resolve, reject) =>{ ... }
            }
        })
    }
    
})

简写方式

//resolve(成功调用), reject(失败调用) 本身又是函数
// 链式编程
new Promise((resolve, reject) =>{
    //第一次网络请求的代码
    网络请求操作(){
        resolve(res , data)
    }.then((res) => {
        //第一次网络请求处理的代码
        .
        .
        .
        //第二次网络请求的代码
        return new Promise((resolve, reject) =>{ 
            网络请求操作(){
            return res+data
            }.then((res) => {
                //第二次网络请求处理的代码
                .
                .
                .
                return res+data
            }
        })
    }
    
})
Promise.all([
    new Promise((resolve, reject) => {
        $ajax({
            url: `url1`,
            success: function(data) {
                resolve(data)
            }
        })
    }
    }
])

Vuex

  • 官方解释:Vuex是一个专门为Vue.js应用程序开发的状态管理模式。
    • 采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化
    • Vuex也集成到Vue的官方调试工具devtools extension,提供了诸如零配置的time-travel调试、状态快照的导入导出等高级调试功能。
  • 可以简单理解为把需要多个组件共享的变量全部存储在一个对象里面,然后将对象放在顶层的Vue示例中,让其他组件可以使用
  • Vuex最大的优点是响应式。
  • state、action(异步操作)、view、
  • mutation(操作),通过$store.commit('mutation中的方法')来修改状态
  • 通过提交mutation的方法,而非直接修改state中的数据,便于更明确的追踪。
  • 不要再mutation中使用异步操作,devtools无法进行跟踪,若需要用异步操作推荐action ,调用mutation使用dispatch()方法
  • Module用于将store分割为模块,每个模块拥有自己的state、mutation、action、getters等,取出时直接再state中取用模块,引用根store时,传递root参数

Axios

简单使用

axios({
    url: 'http://123.207.32.32:8000/home/multidata' 
}).then(res => {
    console.log(res);
})

传参使用

axios({
    url: 'http://123.207.32.32:8000/data?type=sell&page1 
}).then(res => {
    console.log(res);
})

创建axios实例用于区分不同ip的数据请求。

拦截器

请求成功
请求失败
响应成功
响应失败 \