重学巩固你的Vuejs知识 2020-04-08

前沿

置身世外只为暗中观察!!!Hello大家好,我是魔王哪吒!重学巩固你的Vuejs知识体系,如果有哪些知识点遗漏,还望在评论中说明,让我可以及时更新本篇内容知识体系。欢迎点赞收藏!

谈谈你对MVC、MVP和MVVM的理解?

https://github.com/webVueBlog/interview-answe/issues/156

转角遇到Vuejs

  1. 你为啥学习Vuejs
  2. 前端开发的复杂化
  3. Vuejs的特点
  4. 安装Vuejs
  5. 体验Vuejs
  6. MVVM架构:data和Vue对象的分离,Vue中的MVVM

目录:

起步

  1. 插值语法:Mustache,v-once,v-html,v-text,v-pre,v-block。
  2. 绑定属性:v-bind的介绍,v-bind的基础,v-bind的语法糖,绑定class,绑定样式。
  3. 计算属性
  4. 事件监听:v-on介绍,v-on基础,v-on参数,v-on修饰符
  5. 条件和循环:条件渲染,v-show指令,v-if和v-show对比
  6. 表单绑定:基本使用,v-model原理,其他类型,值绑定,修饰符。

组件化开发:

什么是组件化,Vue组件化开发思想

  1. 注册的步骤
  2. 全局和局部组件
  3. 父组件和子组件
  4. 注册组件语法糖
  5. 模板的分离写法
  6. 组件的其他属性
  7. 父级向子级传递
  8. 子级向父级传递
  9. 父子组件的访问
  10. 非父子组件通信

组件化高级语法:

  1. 插槽slot:编译作用域,为什么使用slot,slot的基本使用,slot的具名插槽,slot的作用域插槽。
  2. 动态组件
  3. 异步组件
  4. 组件声明周期

Vue Cli

  1. 什么是webpack
  2. webpack和gulp对比
  3. 手动webpack的配置
  4. Vue Cli是什么
  5. Vue Cli依赖环境
  6. Vue Cli的安装

网络封装

  1. 使用传统的Ajax是基于XMLHttpRequest(XHR)
  2. 使用jQuery-Ajax
  3. Vue-resource
  4. 使用axios

axios的使用

  1. 了解axios:axios请求方式
  2. 发送请求,发送get请求,发送并发请求,axios全局配置,常见配置选项。
  3. axios实例,为什么创建axios实例,如何创建axios实例,axios的封装。
  4. axios的拦截器:请求和响应

vuejs原理相关:响应式原理,源码。

vue.js是什么

  • vue是一套用于构建用户界面的渐进式框架。
  • 从自底向上逐层应用,核心库是只关注图层。
  • 易于学习,便于与第三方库或既有项目整合。

Vue基础语法

对于基础知识需要掌握,简单写写✍

vue.js安装

直接CDN引入:

  1. 对于制作原型或学习

代码:

  1. 对于生产环境

代码:

  1. NPM

代码:

# 最新稳定版
$ npm install vue

vue响应式初体验

声明式编程:

代码:




    
        
        
        
        
    
    
        
{{ a }}

小案例-计算器

  1. 新的属性:methods,该属性是用于Vue对象中定义的方法。
  2. 新的指令:@click,该指令是用于监听某个元素的点击事件,并且需要指定当发生点击时,执行的方法。

代码:

当前计数{{counter}}

Vue中的MVVM

MVVM的思想

  1. view是我们的DOM
  2. Model是我们抽离出来的obj
  3. ViewModel是我们创建的Vue对象实例

它们之间是如何工作的呢?

  1. ViewModel通过Data Binding让obj中的数据实时在DOM中显示
  2. ViewModel通过DOM Listener来监听DOM事件,并且通过methods中的操作,来改变obj中的数据
  • el:类型:string | HTMLElement
  • 作用:决定之后Vue实例会管理哪一个DOM
  • data:类型:Object | Function
  • 作用:Vue实例对应的数据对象
  • methods:类型:{[key:string]:Function}
  • 作用:定义属于Vue的一些方法,可以在其他地方调用,也可以在指令中使用。

什么是Vue的生命周期

生命周期:☞ 事物从诞生到消亡的整个过程

  • release稳定版本
  • debug版本
  1. Mustache语法也就是双大括号
  2. 插值操作
  3. 绑定属性
  4. 计算属性
  5. 事件判断
  6. 循环遍历
  7. 阶段案例
  8. v-model

v-once指令的使用

{{message}}

{{message}}

v-once

  1. 该指令后面不需要跟任何表达式
  2. 该指令表示元素和组件只渲染一次,不会随着数据的改变而改变

v-html

当我们从服务器请求到的数据本身就是一个HTML代码时

  1. 如果直接通过{{}}来输出,会将HTML格式进行解析,并且显示对应的内容。
  2. 可以使用v-html指令
  3. 该指令后跟上一个string类型
  4. 会将stringhtml解析处理并且进行渲染

v-text的作用和Mustache比较相似,独使用于将数据显示在界面中,一般情况下,接受一个string类型。

{{message}}

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

{{message}}

v-cloak斗篷的意思。

hello{{name}}

v-bind的介绍

v-bind用于绑定一个或多个属性值,或者向另一个组件传递props值。




绑定class有两种方式:

  1. 对象语法
  2. 数组语法

对象语法:

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

hello

用法二,传入多个值

hello

用法三:

用法四: 可以放在一个methods或者computed中

hello

v-bind动态绑定class,数组语法

{{mesg}}

{{mesg}}

v-bind动态绑定style

对象语法和数组语法两种绑定。

绑定方法:对象语法:

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

style后面跟的是一个对象类型,对象的keycss属性名称,对象的value是具体赋的值,值可以来自于data中的属性。

绑定方法:数组语法:

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

计算属性的基本属性

计算属性,写在实例的computed选项中:

{{firstName}}{{lastName}}

{{fullName}}

计算属性的缓存:

为什么使用计算属性这个东西?

原因:计算属性会进行缓存,如果多次使用时,计算属性只会调用一次。

setter和getter

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

{{fullName}}
{{firstName}}
{{lastName}}
computed: {
    fullName: function() {
        return this.firstName+" "+this.lastName
    }
    
    // 计算属性一般是没有set方法,只读属性。
    fullName: {
        get: function() {
            return this.firstName + " " + this.lastName
        }
    }
}

const的使用

const的使用,在JavaScript中使用const修饰的标识符为常量,不可以再次赋值。

在es6开发中,优先使用const,只有需要改变一个标识符的时候才使用let。

在使用cost定义标识符,必须进行赋值。

常量的含义是指向的对象不能修改,但是可以改变对象内部的属性。

什么时候使用const呢?

当我们修饰的标识符不会被再次赋值时,就可以使用const来保证数据的安全性。

const的使用:

const a=20;
a = 10; // 错误:不可以修改

const name; // 错误,const修饰的标识符必须赋值

let和var

块级作用域:

JS中使用var来声明一个变量,变量的作用域主要是和函数的定义有关。

对于其他块定义来说是没有作用域的,比如if/for等,开发中往往会引发一些问题。

// 监听按钮的点击
var btns = document.getElementsByTagName('button');
for(var i=0; i
let btns = document.getElementsByTagName('button');
for(let i=0;i

块级作用域

变量作用域:变量在什么范围内是可用的。

var func;
if(true) {
    var name = 'web';
    func = function() {
        console.log(name); // web
    }
    
    func(); // web
}

// name = 'it'
func(); // web -> it
console.log(name); // web -> it

没有块级作用域引起的问题,for的块级

var btns = document.getElementsByTagName('button');
for(var i=0; i

闭包:

var btns = document.getElementsByTagName('button');
for(var i=0; i

为什么闭包可以解决问题,因为函数是一个作用域。

对象的增强写法

属性初始化简写和方法的简写:

// 属性的简写
// es6前
let name = 'web'
let age = 12
let obj1 = {
    name: name,
    age: age,
}
console.log(obj1);
// es6后
let obj2 = {
    name, age
}
console.log(obj2)
// 方法的简写
// es6之前
let obj1 = {
    test: function() {
        console.log('obj1')
    }
}
obj1.test();

// es6后
let obj2 = {
    test() {
        console.log('obj2')
    }
}
obj2.test();

v-on基础

v-on:click="counter++"

点击次数:{{counter}}

let app = new Vue({ el: '#app', data: { counter: 0 }, methods: { btnClick(){ this.counter++ } } })

v-on修饰符的使用

web

Vue提供了一些修饰符:

.stop 调用event.stopPropagation()

.prevent 调用event.preventDefault()

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

.once 只触发一次回调
// 停止冒泡


// 阻止默认行为


// 阻止默认行为,没有表达式
// 串联修饰符 // 键修饰符,键别名 // 键修饰符,键代码 // 点击回调智慧触发一次

v-if,v-else-if,v-else

简单使用:

优秀

良好

及格

不及格

登录切换:

v-for遍历对象

  • {{value}}-{{key}}-{{index}}

组件的Key属性

使用v-for时,给对应的元素或组件添加上一个:key属性。

key的作用主要是为了高效的更新虚拟dom。

数组中哪些方法是响应式的

push()
pop() 删除数组中的最后一个元素
shift() 删除数组中的第一个元素
unshift() 在数组最前面添加元素

splice()
sort()
reverse()

购物车

书籍名称 出版日期 价格 购买数量 操作
{{value}}

表单绑定v-model

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

{{message}}

reduce作用对数组中所有的内容进行汇总。

JavaScript reduce() 方法

var numbers = [65, 44, 12, 4];
 
function getSum(total, num) {
    return total + num;
}
function myFunction(item) {
    document.getElementById("demo").innerHTML = numbers.reduce(getSum);
}

定义和用法

reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。

reduce() 可以作为一个高阶函数,用于函数的 compose。

注意: reduce() 对于空数组是不会执行回调函数的。

语法

array.reduce(function(total, currentValue, currentIndex, arr), initialValue)

v-model的使用以及原理




v-model是语法糖,本质:

  1. v-bind绑定一个value属性
  2. v-on指令给当前元素绑定input事件

代码:




v-model:checkbox

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

单个勾选框:

v-model即为布尔值。inputvalue并不影响v-model的值。

多个复选框:

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

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

v-model:select

select分单选和多选两种情况

单选:只能选中一个值,多选:可以选择多个值。

v-model结合select类型

checkbox一样,select分单选和多选两种情况。

单选,只能选择一个值,v-model绑定的是一个值。当我们选中option中的一个时,会将它对应的value赋值到mySelect中。

多选,可以选中多个值。v-model绑定的是一个数组。当选中多个值时,就会将选中的option对应的value添加到数组mySelects中。

// 选择一个值

您最喜欢的{{mySelect}}

// 选择多个值

您最喜欢的{{mySelects}}

input中的值绑定


修饰符

lazy修饰符:

  1. 默认情况下,v-model默认是在input事件中同步输入框的数据的。
  2. 一旦有数据发生改变对应的data中的数据就会自动发生改变。
  3. lazy修饰符可以让数据在失去焦点或者回车时才会更新。

number修饰符:

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

trim修饰符:

  1. 如果输入的内容首尾有很多空格,通常我们希望将其去除
  2. trim修饰符可以过滤内容左右两边的空格

示例:

{{message}}

什么是组件化

  1. 组件化是vue.js中的重要思想
  2. 它提供了一种抽象,让我们可以开发出一个个独立可复用的小组件来构造我们的应用
  3. 任何的应用都会被抽象成一颗组件树

注册组件的基本步骤:

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

示例:

调用Vue.extend()方法创建组件构造器
调用Vue.component()方法,注册组件
在Vue实例的作用范围内使用组件

组件示例:

全局组件和局部组件

  1. Vue.extend()调用Vue.extend()创建一个组件构造器。
  2. 通常在创建组件构造器时,传入template代表我们自定义组件的模板。
  3. 该模板在使用到组件的地方,显示的html代码。
  4. 这种写法在Vue2.x的文档几乎看不到了。
  5. Vue.component()是将刚才的组件构造器注册为一个组件,并且给它起一个组件的标签名称。
  6. 注册组件的标签名,组件构造器。

示例:

组件标题

示例:

父组件和子组件

组件树

  1. 组件和组件之间存在层级关系
  2. 其中一种非常重要的关系就是父子组件的关系

示例:

// 注册局部组件的语法糖 const app = new Vue({ el: '#app', data: { message: 'web' }, components: { 'cpn2': { template: `

web

` } } })

vue简化了注册组件的方式,提供了注册的语法糖。

组件模板抽离的写法

vue提供了两种定义html模块内容:

  1. 使用

    template标签

    
    // 注册一个全局组件
    Vue.component('cpn', {
     template: '#cpn'
    })
    

    组件可以访问vue实例数据吗

    组件是一个单独的功能模块封装,有属于自己的html模板和自己的数据data

    组件对象有一个data属性,methods属性,这个data属性必须是一个函数,函数返回一个对象,对象内部保存着数据。

    Vue 中,父子组件的关系

    props向下传递,事件向上传递。

    父组件通过 props给子组件下发数据,子组件通过事件给父组件发送消息。

    props支持的数据类型:

    String
    
    Number
    
    Boolean
    
    Array
    
    Object
    
    Date
    
    Function
    
    Symbol
    

    示例:

    Vue.component('my-component',{
     props: {
         // 基础的类型检查
         propA: Number,
         // 多个可能的类型
         propB: [String, Number],
         // propC: {
             type: String,
             required: true
         },
         // 带有默认值的数字
         propD: {
             type: Number,
             default: 100
         },
         // 带有默认值的对象
         propE: {
             type: Object,
              default: function(){
                  return {message: 'web'}
              }
         },
         // 自定义验证函数
         propF: {
             vfunc: function(value) {
                 return value > 1
             }
         }
     }
    })
    

    子传父

    代码:

    this.$emit('item-click',item)
    

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

    自定义事件:

    1. 在子组件中,通过$emit()来触发事件。
    2. 在父组件中,通过v-on来监听子组件事件。

    自定义事件代码:

    点击次数

    let app = new Vue({ el: '#app', data: { total: 0 }, methods: { changeTotal(counter) { this.total = counter } }, components: { 'child-cpn': { template: '#childCpn', data(){ return{ counter: 0 } }, methods: { increment(){ this.counter++; this.$emit('increment', this.counter) }, decrement(){ this.counter--; this.$emit('decrement',this.counter) } } } } })

    父子组件的访问方式:$children

    有时候需要父组件直接访问子组件,子组件直接访问父组件,或者是子组件访问父组件。

    1. 父组件访问子组件,使用$children或者$refs
    2. 子组件访问父组件,使用$parent

    对于$children的访问:

    1. this.$children是一个数组类型,它包含所有子组件对象。
    2. 通过遍历,取出所有子组件的message状态。

    示例:

    // 父组件template // 子组件 // 子组件 Vue.component('parent-cpn',{ template: '#parentCpn', methods: { showChildCpn(){ for(let i=0; i

    父子组件的访问方式:$parent

    子组件中直接访问父组件,可以通过$parent

    1. 虽然可以通过$parent来访问父组件,但是尽量不要这样做
    2. 子组件应该尽量避免直接访问父组件的数据,因为这样耦合度太高了。

    父子组件的访问方式$refs

    $children的缺陷:

    1. 通过$children访问子组件,是一个数组类型,访问其子组件要通过索引值。
    2. 子组件过多时,需要拿其中一个时,不能确定它的索引值,还可能发生变化。
    3. 获取其中一个特定的组件,可以使用$refs

    $refs的使用:

    1. $refsref指令通常一起使用
    2. 通过ref给某个子组件绑定一个特定的id
    3. 通过this.$refs.id可以访问到该组件

    示例:

    
    
    
    
    show() {
        console.log(this.$refs.child1.message);
        console.log(this.$refs.child2.message);
    }
    

    看看一个.vue文件项目

    
    
    
    
    
    

    三层部分:

    slot插槽的使用

    vue中的代码slot是什么呢,它叫插槽,元素作为组件模板之中的内容分发插槽,传入内容后元素自身将被替换。

    v-slot用法:

    1. 默认插槽
    2. 具名插槽
    3. 作用域插槽
    4. slot以及slot-scope的用法:子组件编写,父组件编写

    默认插槽

    子组件:

    // 子组件
    
    

    slot基本使用

    1. 在子组件中,使用可以为子组件开启一个插槽。
    2. 该插槽插入什么内容取决于父组件如何使用。

    子组件定义一个插槽:

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

    示例:

    web

    使用具名插槽

    1. slot元素添加一个name属性

    示例:

    // 没有任何内容 // 传入某个内容 left left center right

    编译作用域

    Vue实例属性:

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

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

    模块化开发

    什么是模块化,将一组模块以正确的顺序拼接到一个文件中的过程,模块是实现特定功能的一组属性和方法的封装。

    利用构造函数封装对象

    function web() {
        var arr = [];
        this.add = function(val) {
            arr.push(var)
        }
        this.toString = function() {
            return arr.join('')
        }
    }
    var a = new web();
    a.add(1); // [1]
    a.toString(); // "1"
    a.arr // undefined
    

    示例:

    var ModuleA = (function(){
        // 定义一个对象
        var obj = {}
        // 在对象内部添加变量和方法
        obj.flag = true
        obj.myFunc = function(info) {
            console.log(info)
        };
        // 将对象返回
        return obj
    }
    
    if(ModuleA.flag) {
        console.log('web')
    }
    
    ModuleA.myFunc('webweb')
    

    常见的模块化规范:

    CommonJS,AMD,CMD,ES6中的Modules

    什么是AMD,异步模块定义,它是在浏览器端实现模块化开发的规范,但是该规范不是原生js支持的,使用AMD规范进行开发的时候需要引入第三方的库函数,就是RequireJS

    RequireJS解决了多个js文件可能有依赖的关系,被依赖的文件需要早于依赖它的文件加载到浏览器;js加载的时候浏览器会停止页面渲染,加载文件越多,页面就会失去响应时间越长。

    CMD是什么,它是通用模块定义,解决的问题和AMD一样,不过在模块定义方式和模块加载时机上不同,CMD需要额外的引入第三方的库文件SeaJS

    JavaScript模块化编程

    1. 可以解决项目中的全局变量污染问题
    2. 开发效率高,利于多人协同开发
    3. 职责单一,方便代码复用和维护
    4. 解决了文件的依赖问题

    那么什么是模块化呢

    将一个项目按照功能划分,理论上一个功能一个模块,互不影响,在需要的时候载入,尽量遵循高内聚低耦合。

    了解CommonJS

    CommonJS 是一种思想,本质上是可复用的JavaScript,它导出特定的对象,提供其它程序使用。

    使用module.exportsexports.obj来导出对象,并在需要它的程序中使用require('module')加载。

    模块化的核心就是:导入和导出

    导出:CommonJS

    module.exports = {
        flag: true,
        test(a,b) {
            return a+b
        },
        demo(a,b) {
            return a*b
        }
    }
    

    导入:CommonJS

    // CommonJS模块
    let {test, demo, flag} = require('moduleA');
    
    // =>
    let ma = require('moduleA');
    let test = ma.test;
    let demo = ma.demo;
    let flag = ma.flag;
    

    因为网站开发越来越复杂,js文件又很多,就会出现一些问题:

    1. 变量名冲突
    2. 文件依赖复杂度高
    3. 页面加载过多,不利于维护

    CommonJS,定义模块,一个单独的js文件就是一个模块,每个模块都有自己单独的作用域,在该模块内部定义的变量,无法被其他模块读取,除了定义为global对象的属性。

    模块的导出:exportsmodule.exports

    模块的导入:require

    1. node中,每个模块内部都有要给自己的module对象
    2. module对象中,有一个成员exports也是一个对象
    3. 通过exports对象导出当前方法或变量,也可通过module.exports导出
    4. node简化了操作,exports等于module.exports,相当于var exports = module.exports

    es模块的导入和导出

    export function add(num1, num2) {
        return num1 + num2
    }
    
    export function accString(param) {
        if (param == 0) {
            return '关'
        }else if(param == 1) {
            return '开'
        }
    }
    
    import {
            accString
        } from '../../utils'
    
    const name = 'web'
    
    export default name
    

    export default

    一个模块中包含某个功能,如果不希望给功能命名,可以让导入者自己来定:

    export default function(){
        console.log('web')
    }
    

    使用:

    import myFunc from '../web.js'
    
    myFunc()
    

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

    import使用

    export指令导出了模块对外提供的接口

    import指令用于导入模块中的内容

    import {name, age} from './web.js'
    

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

    import * as web from './web.js'
    
    console.log(web.name);
    

    生命周期

    首先:new Vue(),new一个Vue的实例,Observe data数据查看,init Events绑定事件,created执行created方法,判断是否有el属性,如果没有,vm.$mount(el)表示处于未挂载状态,可以手动调用这个方法来挂载。判断是否有template属性。

    如果有el属性,判断是否有template属性。

    实例化期和加载期
    创建期间的生命周期函数:beforeCreatecreatedbeforeMountmounted

    beforeCreate在实例初始化后,数据观测data observerevent/watcher事件配置之前被调用。

    更新期

    运行期间的生命周期函数:beforeUpdateupdated

    created实例已经创建完成后被调用。

    实例已完成以下的配置:数据观测data observer,属性和方法的运算,watch/event事件回调。

    挂载阶段还没开始,$el属性目前不可见。

    beforeMount在挂载开始之前被调用,相关的render函数首次被调用。mountedvm.$el已经挂载在文档内,对已有dom节点的操作可以在期间进行。beforeUpdate数据更新时调用,发生在虚拟dmo重新渲染和打补丁之前。updated当这个钩子被调用时,组件dom已经更新,所以你现在可以执行依赖于dom的操作。activateddeactivatedbeforeDestroydestroyed。实例销毁之前调用,vue实例销毁后调用。

    卸载期

    销毁期间的生命周期函数:beforeDestroydestroyed

    实例生命周期钩子

    每个vue实例在被创建时都要经过一系列的初始化过程,需要设置数据监听,编译模板,将实例挂载到dom并在数据变化时更新dom等,同时在这个过程中也会运行一些叫做生命周期钩子的函数。

    用于给用户在不同阶段添加自己代码的机会。

    beforeCreate,此时的data是不可见的

    data() {
        return {
            a: 1
        }
    },
    beforeCreate() {
        // red
        console.log(this.a); // 看不见
    }
    

    created实例已经创建完成后被调用,这个时候你看不见你页面的内容,实例已完成表示:数据观测data observer,属性和方法的运算,watch/event事件回调。

    这个时候挂载阶段还没开始,$el属性目前不可见。

    export default {
        data() {
            return {
                a: 1
            }
        },
        beforeCreate() {
            console.log(this.a);
        },
        created() {
            // red
            console.log(this.a);
            console.log(this.$el);
            // 此时data数据里面的a可见,this.$el不可见
        }
    }
    

    beforeMount在挂载开始之前被调用,相关的render函数首次被调用。

    export default{
        data() {
            return {
                a: 1
            }
        },
        beforeCreate() {
            console.log(this.a); // 不可见
        },
        created() {
            console.log(this.a);
            console.log(this.$el); // 不可见
        },
        beforeMount() {
            console.log(this.$el); // 不可见
        }
    }
    

    mounted

    export default {
        data() {
            return {
                a: 1
            }
        },
        mounted() {
            console.log(this.$el); // 此时$el 可见
        }
    }
    

    beforeUpdate钩子,dom更新之前调用:

    beforeUpdate() {
        console.log(this.a);
    }
    
    // document.getElementById("web").innerHTML
    

    updated钩子,dom更新之后调用:

    updated() {
        console.log(this.a);
    }
    
    // document.getElementById("web").innerHTML
    

    activateddeactivated(组件)

    activated() {
        console.log("组件使用了");
    },
    
    deactivated() {
        console.log("组件停用了");
    Data to Drag},
    

    keep-alivevue的内置组件,能在组件切换过程中将状态保留在内存中,防止重复渲染dom

    包裹动态组件时,会缓存不活动的组件实例,而不会销毁它们。和相似,是一个抽象组件:它自身不会渲染一个DOM元素,也不会出现在父组件链中。

    当组件在内被切换,它的activateddeactivated这两个生命周期钩子函数将会被对应指定。

    它的使用是因为我们不希望组件被重新渲染而影响使用体验,或者是性能,避免多次渲染降低性能。缓存下来,维持当前得状态。

    场景:

    1. 商品列表页点击商品跳转到商品详情,返回后仍显示原有信息
    2. 订单列表跳转到订单详情,返回,等等场景。

    keep-alive生命周期:

    初次进入时:created > mounted > activated;退出后触发 deactivated;再次进入:会触发activated;事件挂载的方法等,只执行一次的放在mounted中;组件每次进去执行的方法放在 activated 中。

    app.vue父组件:

    
    
    
    

    beforeDestroydestroyed

    beeforeDestroy类型为function,详细:实例销毁之前调用,在这一步,实例仍然完全可用。

    该钩子在服务器端渲染期间不被调用。

    destroyed类型为function,详细:vue实例销毁后调用,调用后,vue实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。

    该钩子在服务器端渲染期间不被调用。

    beforeRouteEnterbeforeRouteLeave

    beforeRouteEnter() {
        console.log('beforeRouteEnter')
    },
    
    beforeRouteLeave() {
        console.log('beforeRouteLeave')
    }
    

    vue路由使用的,路由进去和路由离开的时候添加的。

    created() {
        console.log('开始执行created钩子函数')
        // 获取data数据
        console.log('获取created属性'+this.value)
        // 获取页面元素
        console.log(this.$refs['example'])
        this.$nextTick(()=>{
            console.log('执行created创建的this.$nextTick()函数')
        })
    },
    
    mounted() {
        console.log('开始执行mounted钩子函数')
        // 获取挂载数据
        console.log('获取挂载数据--'+this.$refs['example'].innerText)
        this.$nextTick(()=>{
            console.log('执行mounted创建的this.$nextTick()函数')
        })
    },
    
    methods: {
        // 更新数据
        updateDate(){
            
        },
        get(){
            this.value='更新data内的value属性值'
            // 获取页面元素数据
            console.log(this.$refs['example').innerText)
            this.$nextTick(()=>{
              console.log(this.$refs['example'].innerText)  
            })
        }
    }
    

    var vm=new Vue({})表示开始创建一个Vue的实例对象,init events&liftcycle表示刚初始化了一个vue空的实例对象,这个时候,对象身上,只有默认的一些生命周期函数和默认事件,其他东西都没有创建,beforeCreate生命周期函数执行的时候,datamethods中的数据都没有初始化。在created中,datamethods都已经被初始化好了,如果要调用methods中的方法,或者操作data中的数据,只能在created中操作。然后vue开始编辑模板,把vue代码中的那些指令进行执行,最终在内存中生成一个编译好的最终模板字符串,渲染为内存中的dom,此时只是在内存中,渲染好了模板,并没有把模板挂载到真正的页面中去。beforeMount函数执行的时候,模板已经在内存中编译好了,但是尚未挂载到页面中去。create vm.$el and replace 'el' with it这一步是将内存中编译好的模板,真实的替换到浏览器的页面中去。mounted,只要执行完了mounted,就表示整个vue实例已经初始化完了。此时,组件从创建阶段进入到了运行阶段。

    beforeUpdate执行的时候,页面中显示的数据还旧的,而data数据是最新的,页面尚未和最新的数据保持同步。updated事件执行的时候,页面和data数据已经保持同步了,都是新的。virtual dom re-render and patch执行,先根据data中最新的数据,在内存中,重新渲染出一份最新的内存dom树,当最新的内存dom树被更新之后,会把最新的内存dom树,重新渲染到真实的页面中,完成数据从dataview的跟新。

    beforeDestroy钩子函数执行时,vue实例就从运行阶段,进入到了销毁阶段。此时的实例还是可用的阶段,没有真正执行销毁过程。destroyed函数执行时,组件已经被完全销毁了,都不可用了。

    vue面试题

    谈一谈你对mvvm的理解

    双向绑定的过程

    视图view,路由-控制器Controller,数据Model

    view->domviewModelModel数据

    传统的mvc指用户操作会请求服务器端路由,路由会调用对应的控制器来处理,控制器会获取数据,将结果返回给前端,让页面重新渲染。

    mvvm,对于传统的前端会将数据手动渲染到页面上,mvvm模式不需要用户收到操作dom元素,将数据绑定到viewModel层上,会自动将数据渲染到页面中,视图变化会通知viewModel层更新数据。

    Vue响应式原理

    1. vue内部是如何监听message数据的改变
    2. 当数据发生改变,vue是如何知道要通知哪些人,界面发生刷新

    核心:

    • Object.defineProperty,监听对象属性的改变
    • 发布订阅者模式

    代码:

    Object.keys(obj).forEach(key => {
     let value = obj[key]
     
     Object.defineProperty(obj, key, {
        set(newValue) {
            // 监听改变
            value = newValue
        },
        get() {
            return value
        }
     })
    })
    
    obj.name = 'web'
    

    发布者订阅者

    class Dep {
        constructor() {
            this.subs = []
        }
    }
    
    class Watcher {
        constructor(name) {
            this.name = name;
        }
    }
    

    对象的Object.defindeProperty中的访问器属性中的getset方法

    • 把数据转化为gettersetter,建立watcher并收集依赖。

    说明:

    watcher通过回调函数更新view;observer观测data数据,通过get通知dep收集watcherdep通过notify()通知watcher数据更新,watcher通过addDep()收集依赖。

    Observer:用于监听劫持所有data属性,dep,watcher,viewCompile解析el模板中的指令。

    依照下图(参考《深入浅出vue.js》)

    首先从初始化data数据开始,使用Observer监听数据,个体每个数据属性添加Dep,并且在Data,有两个gettersetter。在它的getter过程添加收集依赖操作,在setter过程添加通知依赖的操作。

    在解析指令或者给vue实例设置watch选项或者调用$watch时,生成对应的watcher并收集依赖。

    Data通过Observer转换成了getter/setter的形式,来对数据追踪变化。

    修改对象的值的时候,会触发对应的settersetter通知之前依赖收集得到的 Dep 中的每一个Watcher,告诉它们值改变了,需要重新渲染视图。

    数据双向绑定原理

    什么是响应式的原理

    1. 核心:Object.defineProperty
    2. 默认vue在初始化数据时,会给data中的属性使用Object.defineProperty重新定义所有属性,当页面取到对应属性时,会进行依赖收集,如果属性发生变化会通知相关依赖进行更新操作。

    initData初始化用户传入的data数据,new Observer将数据进行观测,this.walk(value)进行对象的处理,defineReactive循环对象属性定义响应式变化,Object.defineProperty,使用Object.defineProperty重新定义数据。

    使用使用Object.defineProperty重新定义数据的每一项。

    Object.defineProperty(obj,key,{
     enumerable: true,
     configurable: true,
     get: function reactiveGetter(){
         const value=getter?getter.call(obj):val
         if(Dep.target){
             dep.depend()
             if(childOb){
                 childOb.dep.depend()
                 if(Array.isArray(value)){
                     dependArray(value)
                 }
             }
         }
         return value
     },
     set: function reactiveSetter(newVal) {
         const value=getter?getter.call(obj).val
         if(newVal === value || (newVal !== newVal && value !==value)){
             return
         }
         if(process.env.NODE_ENV !== 'production' && customSetter){
             customSetter()
         }
         val = newVal
         childOb = !shallow && observe(newVal)
         dep.notify()
     }
    })
    

    vue中式如何检测数组变化

    使用函数劫持的方式,重写了数组的方法,vuedata中的数组进行了原型链的重写,指向了自己定义的数组原型方法,这样当调用数组api时,可以通知依赖跟新,如果数组中包含着引用类型,会对数组中的引用类型再次进行监控

    initData初始化用户传入的data数据,new Observer将数据进行观测,protoAugment(value,arrayMethods)将数据的原型方法指向重写的原型。

    • 对数组的原型方法进行重写
    • observerArray深度观察数组中的每一项

    代码:

    if(Array.isArray(value)){
        // 判断数组
        if(hasProto){
            protoAugment(value, arrayMethods)// 改写数组原型方法
        }else{
            copyAugment(value,arrayMethods,arrayKeys)
        }
        this.observeArray(value)
        //深度观察数组中的每一项
    }else{
        this.walk(value) 
        // 重新定义对象类型数据
    }
    
    function protoAugment(target, src: Object){
        target.__proto__ = src
    }
    
    export const arrayMethods = Object.create(arrayProto)
    const methodsToPatch=[
     'push',
     'pop',
     'shift',
     'unshift',
     'splice',
     'sort',
     'reverse'
    ]
    
    methodsToPatch.forEach(function (method){
        const original = arrayProto[method]
        def(arrayMethods, method, function mutator(...args){
            const result = original.apply(this.args)
            const ob = this.__ob__
            let inserted
            switch(method) {
                case 'push':
                case 'unshift':
                inserted = args
                break
                case 'splice':
                inserted = args.slice(2)
                break
            }
            if(inserted) ob.observerArray(inserted)
            // 对插入的数据再次进行观测
            ob.dep.notify()
            // 通知视图更新
            return result
        }
    }
    
    observeArray(items: Array) {
        for(let i=0, l = items.length; i<1; i++) {
            observe(item[i])
            // 观测数组中的每一项
        }
    }
    

    为什么vue采用异步渲染

    如果不采用异步更新,每次更新数据都会对当前组件进行重新渲染,为了性能考虑。

    dep.notify()通知watcher进行更新操作,subs[i].update()依次调用watcherupdatequeueWatcherwatcher去重放到队列中,nextTick(flushSchedulerQueue)异步清空watcher队列。

    nextTick实现原理

    微任务高于宏任务先执行

    nextTick方法主要使用了宏任务和微任务,定义了一个异步方法,多次调用了nextTick会将方法存入到队列中,通过这个异步方法清空当前队列。

    nextTick方法是异步方法。

    原理:nextTick(cb)调用nextTick传入cbcallbacks.push(cb)将回调存入数组中,timerFunc()调用timerFunc,返回promise支持promise的写法。

    webpack

    什么是webpack,webpack是一个现代的JavaScript应用的静态模块打包工具。

    webpack是前端模块化打包工具

    安装webpack需要安装node.js,node.js自带有软件包管理工具npm

    全局安装

    npm install [email protected] -g
    

    局部安装

    npm install [email protected] --save-dev
    

    webpack.config.js固定名文件:

    const path = require("path")
    module.exports = {
        entry: './src/main.js',
        output: {
            patch: './dist',
            filename: ''
        },
    }
    

    package.json

    {
        "name": 'meetwebpack',
        "version": "1.0.0",
        "description": "",
        "main": "index.js",
        "scripts": {
            "test": "echo ..."
        },
        "author": "",
        "license": "ISC"
    }
    

    什么是loader

    loaderwebpack中一个非常核心的概念

    loader使用过程:

    1. 通过npm安装需要使用的loader
    2. webpack.config.js中的moudules关键字下进行配置

    package.json中定义启动

    {
        "name": "meetwebpack",
        "version": "1.0.0",
        "description": "",
        "main": "index.js",
        "scripts": {
            "build": "webpack"
        },
        "author": "",
        "license": "ISC",
        "devDependencies": {
            "webpack": "^3.6.0"
        }
    }
    

    webpack的介绍

    webpack可以看做是模块打包机,它可以分析你的项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言,将其打包为合适的格式以供浏览器使用。

    可以实现代码的转换,文件优化,代码分割,模块合并,自动刷新,代码校验,自动发布。

    安装本地的webpack

    webpack webpack-cli -D
    

    初始化:

    yarn init -y
    
    yarn add webpack webpack-cli -D
    

    webpack可以进行0配置,它是一个打包工具,可以输出后的结果(Js模块),打包(支持js的模块化)

    运行webpack命令打包

    npx webpack
    

    webpack.config.jswebpacknode写出来的node的写法:

    let path = require('path')
    console.log(path.resolve('dist');
    
    module.exports = {
        mode: 'development',
        // 模式,默认两种,production,development
        entry: '' // 入口
        output: {
            filename: 'bundle.js',
            // 打包后的文件名
            path: path.resolve(__dirname, 'build'),
            // 把相对路径改写为绝对路径
        }
    }
    

    自定义,webpack.config.my.js

    使用命令:

    npx webpack --config webpack.config.my.js
    

    package.json:

    {
        "name": 'webpack-dev-1',
        "version": "1.0.0",
        "main": "index.js",
        "license": "MIT",
        "scripts": {
          "build": "webpack --config webpack.config.my.js"  
        },
        "devDependencies": {
            "webpack": "^4.28.3",
            "webpack-cli": "^3.2.0"
        }
    }
    

    使用命令:

    npm run build
    
    // npm run build -- --config webpack.config.my.js
    

    开发服务器的配置

    代码:

    let path = require('path')
    let HtmlWebpackPlugin = require('html-webpack-plugin')
    console.log(path.resolve('dist');
    
    module.exports = {
        devServer: {
          // 开发服务器的配置  
          port: 3000,
          // 看到进度条
          progress: true,
          contentBase: "./build",
          compress: true
        },
        mode: 'development',
        // 模式,默认两种,production,development
        entry: '' // 入口
        output: {
            filename: 'bundle.js',
            // 打包后的文件名
            path: path.resolve(__dirname, 'build'),
            // 把相对路径改写为绝对路径
        },
        plugins: [
            // 数组,所有的webpack插件
            new HtmlWebpackPlugin({
                template: './src/index.html',
                filename: 'index.html',
                minify:{
                    removeAttributeQuotes: true,//删除“”
                    collapseWhitespace: true, // 变成一行
                   
                },
                 hash: true
            })
        ],
        module: {
            // 模块
            rules: [
                // 规则
                {test: /\.css$/, use: [{
                    loader: 'style-loader',
                    options: {
                        insertAt: 'top'
                    }
                },'css-loader'] },
            ]
        }
    }
    
    output: {
        filename: 'bundle.[hash:8].js',// 打包文件名后只显示8位
    }
    
    {
        "name": 'webpack-dev-1',
        "version": "1.0.0",
        "main": "index.js",
        "license": "MIT",
        "scripts": {
          "build": "webpack --config webpack.config.my.js",
          "dev": "webpack-dev-server"
        },
        "devDependencies": {
            "webpack": "^4.28.3",
            "webpack-cli": "^3.2.0"
        }
    }
    
    yarn add css-loader style-loader -D
    

    样式:

    1. style-loader将模块的导出作为样式添加到dom
    2. css-loader解析css文件后,使用import加载,并且返回css代码
    3. less-loader加载和转译less文件
    4. sass-loader加载和转译sass/scss文件
    5. postcss-loader使用PostCSS加载和转译css/sss文件
    6. stylus-loader加载和转译Stylus文件

    style-loader安装:

    npm install style-loader --save-dev
    

    用法:

    建议将style-loadercss-loader结合使用

    component.js

    import style from './file.css'
    
    1. css-loader只负责将css文件进行加载
    2. style-loader负责将样式添加到dom
    3. 使用多个loader时,是从右到左

    代码:

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

    css文件处理:style-loader

    安装style-loader

    npm install --save-dev style-loader
    

    style-loader需要放在css-loader的前面,webpack在读取使用的loader的过程中,是按照从右向左的顺序读取的。

    webpack.config.js的配置如下:

    const path = require('path')
    
    module.exports = {
        // 入口:可以是字符串/数组/对象,这里我们入口只有一个,所以写一个字符串即可。
        entry: './src/main.js',
        // 出口:通常是一个对象,里面至少包含两个重要属性,path和filename
        output:{
            path: path.resolve(__dirname, 'dist'), // 注意:path通常是一个绝对路径
            filename: 'bundle.js'
        },
        module: {
            rules: {
                {
                    test: /\.css$/,
                    use: ['style-loader','css-loader']
                }
            }
        }
    }
    

    webpack less文件处理

    安装:

    npm install --save-dev less-loader less
    

    示例:

    css-loader,style-loader,less-loader链式调用,可以把所有样式立即应用于dom

    // webpack.config.js
    module.exports = {
        ...
        rules: [{
            test: /\.less$/,
            use: [{
                loader: 'style-loader'
            },{
                loader: 'css-loader'
            },{
                loader: 'less-loader'
            }]
        }]
    }
    

    图片文件处理

    css normal代码:

    body {
        background: url("../img/test.jpg")
    }
    

    url-loader

    npm install --save-dev url-loader
    

    用法

    url-loader功能类似于file-loader,但是在文件大小低于指定的限制时,可以返回一个DataURL

    import img from './image.png'
    

    webpack.config.js

    module.exports = {
        module: {
            rules: [
                {
                    test: /\.(png|jpg|gif)$/,
                    use: [
                        {
                            loader: 'url-loader',
                            options: {
                                limit: 8192
                            }
                        }
                    ]
                }
            ]
        }
    }
    

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

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

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

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

    es6转es5的babel

    如果希望es6转成es5,那么就需要使用babel

    npm install --save-dev babel-loader@7 babel-core babel-preset-es2015
    

    配置webpack.config.js文件:

    {
        test: /\.m?js$/,
        use: {
            loader: 'babel-loader',
            options: {
                presets: ['es2015']
            }
        }
    }
    

    使用vue

    如何在我们的webpack环境中集成vue.js

    代码:

    npm install vue --save
    
    1. runtime-only代码中,不可以有任何的template
    2. runtime-compiler代码中,可以有template因为有compiler可以用于编译template

    spa(simple age web application)->vue-router(前端路由)

    .vue文件封装处理

    安装vue-loadervue-template-compiler

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

    认识webpack的plugin

    1. plugin是什么?
    • plugin是插件的意思,通常用于对某个现有的架构进行扩展。
    • webpack中的插件,是对webpack现有功能的各种扩展。
    1. loaderplugin的区别
    • loader主要用于转换某些类型的模块,它是一个转换器。
    • plugin是插件,它是对webpack本身的扩展,是一个扩展器。
    1. plugin的使用过程:
    • 通过npm安装需要使用的plugins
    • webpack.config.js中的plugins中配置插件

    webpack.config.js的文件:

    查看bundle.js文件的头部:

    Vue Cli详解

    什么是vue cliCommand-Line Interface,命令行界面,俗称脚手架,vue cli是一个官方发布的项目脚手架。使用vue-cli可以快速搭建vue开发环境以及对应的webpack配置。

    vue cli的使用

    安装vue脚手架

    npm install -g @vue/cli
    

    vuecli2初始化过程

    代码:

    vue init webpack vuecli2test
    
    1. 根据名称创建一个文件夹,存放之后项目的内容,该名称会作为默认的项目名称,但是不能包含大写字母等
    2. Project name 项目名称,不能包含大写
    3. Project description项目描述
    4. Author作者信息
    5. Vue build``runtime
    6. Install vue-router``no是否安装等

    目录结构详解

    build``configwebpack相关配置,node_modules 是依赖的node相关的模块,src是写代码地方。 .babelrc是es代码相关转换配置,.editorconfig项目文本相关配置,.gitignore``git仓库忽略的文件夹配置,.postcssrc.jscss相关转化的配置。

    .editorconfig

    前端模块化:

    为什么使用模块化,简单写js代码带来的问题,闭包引起代码不可复用,自己实现了简单的模块化,es中模块化的使用:exportimport

    npm install @vue/cli -g
    
    npm clean cache -force
    

    vue cli2初始化:

    vue init webpack my-project
    

    vue cli3初始化项目:

    vue create my-project
    

    箭头函数的使用和this

    箭头函数,是一种定义函数的方式

    1. 定义函数的方式:function
    const a = function(){
        
    }
    
    1. 对象字面量中定义函数
    const obj = {
        b: function() {
            
        },
        b() {
            
        }
    }
    
    1. 箭头函数
    const c = (参数列表) => {
        
    }
    const c = () => {
        
    }
    

    箭头函数参数和返回值

    代码:

    const sum = (num1, num2) => {
        return num1 + num2
    }
    
    const power = (num) => {
        return num * num
    }
    
    const num = (num1,num2) => num1 + num2
    
    const obj = {
        a() {
            setTimeout(function() {
                console.log(this); // window
            })
            setTimeout(()=>{
              console.log(this); // obj对象 
            })
        }
    }
    

    路由,,vue-router基本使用,vue-router嵌套路由,vue-router参数传递,vue-router导航守卫。

    路由是一个网络工程里面的术语,路由就是通过互联的网络把信息从源地址传输到目的地址的活动。

    路由器提供了两种机制:路由和转送。路由是决定数据包从来源到目的地的路径,转送将输入端的数据转移到合适的输出端。路由中有一个非常重要的概念叫路由表。路由表本质上就是一个映射表,决定了数据包的指向。

    后端路由:后端处理url和页面之间的映射关系。

    前端路由和后端路由,前端渲染和后端渲染

    vue-routerkoa-router的区别:

    vue-router是前端路由,koa-router是后端路由。

    vue-router前端路由原理:

    前端路由主要模式:hash模式和history模式。

    路由的概念来源于服务端,在服务端中路由描述的是 URL 与处理函数之间的映射关系。

    前后端渲染之争

    url中的hashhtml5history

    前端路由的核心是改变url,但是页面不进行整体的刷新。单页面,其实spa最重要的特点就是在前后端分离的基础上加了一层前端路由。就是前端来维护一套路由规则。

    urlhash

    urlhash是锚点#,本质上是改变window.locationhref属性。直接赋值location.hash来改变href,但是页面不发生刷新。

    html5history模式:pushState

    html5history模式:replaceState

    html5history模式:go

    history.go()

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

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

    安装vue-router

    npm install vue-router --save
    
    1. 导入路由对象,并且调用Vue.use(VueRouter)
    2. 创建路由实例,并且传入路由映射配置
    3. Vue实例中挂载创建的路由实例

    代码:

    // 配置路由相关的信息
    import VueRouter from 'vue-router'
    import vue from 'vue'
    import Home from '../components/Home'
    import About from '../components/About'
    
    // 通过Vue.use(插件),安装插件
    Vue.use(VueRouter)
    
    // 配置路由和组件之间的应用关系
    const routes = [
     {
         path: '/home',
         component: Home
     },
     {
         path: '/about',
         component: About
     }
    ]
    
    // 创建VueRouter对象
    const router = new VueRouter({
     routes
    })
    
    // 将router对象传入到`Vue`实例
    export default router
    

    main.js

    import Vue from 'vue'
    import App from './App'
    import router from './router'
    
    Vue.config.productionTip = false
    
    new Vue({
     el: '#app',
     router,
     render: h => h(App)
    })
    

    使用vue-router的步骤

    1. 创建路由组件
    2. 配置路由映射:组件和路径映射关系
    3. 使用路由:通过

    代码:

    组件components

    // home
    
    
    
    
    
    
    
    
    
    
    
    

    App.vue