前沿

置身世外只为暗中观察!!!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. 对于制作原型或学习

代码:<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

  1. 对于生产环境

代码:<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>

  1. NPM

代码:

# 最新稳定版
$ npm install vue

vue响应式初体验

声明式编程:

代码:

<!DOCTYPE html>
<!-- 魔王哪吒 -->
<html><head><meta charset="utf-8"><title></title><script src="vue.js" type="text/javascript" charset="UTF-8"></script><!-- <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> --></head><body><div id="app">{{ a }}</div><script type="text/javascript">// 我们的数据对象var data = { a: 1 };// 该对象被加入到一个 Vue 实例中var vm = new Vue({el: "#app",data: data});// data.a = "dada"vm.a = "qianduan";data.a == vm.a;</script></body>
</html>

小案例-计算器

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

代码:

<div id="app"><h1>当前计数{{counter}}</h1><button @click="increment">+</button><button @click="decrement">-</button>
</div><script src="../js/vue.js"></script><script>let app = new Vue({el: '#app',data: {counter: 0},methods: {increment(){this.counter++},decrement(){this.counter--},}})
</script>

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指令的使用

<div id="app"><h1>{{message}}</h1><h2 v-once>{{message}}</h2>
</div>

v-once

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

v-html

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

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

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

<div id="app"><h2 v-text="message"></h2><h2>{{message}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>let vm = new Vue({el: '#app',data: {message: '你好'}})
</script>

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

<div id="app"><p v-pre>{{message}}</p>
</div>
<script src="../js/vue.js"></script>
<script>let app = new Vue({el: '#app',data: {message: 'hello'}})
</script>

v-cloak斗篷的意思。

<div id="app"><h2 v-cloak>hello{{name}}</h2>
</div>
<script>setTimeout(()=>{let app = new Vue({el: '#app',data: {name: 'web'}})},10000)
</script><style>[v-cloak] {display: none;}
</style>

v-bind的介绍

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

<div id="app"><a v-bind:href="link">vuejs</a><img v-bind:src="url" alt="">
</div><script>let app = new Vue({el: '#app',data: {}})

v-bind语法糖

v-bind有一个对应的语法糖,就是简写方式

<div id = "app"><a :href="link">vuejs</a><img :src="longURL" alt="">
</div>

v-bind动态绑定class

<style>.active{color: red;}
</style><div id="app"><h1 class="active">{{message}}</h2>
</div><script>const app = new Vue({el: '#app',data: {message: 'hello'}})
</script>

绑定class有两种方式:

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

对象语法:

用法一:直接通过{}绑定一个类
<h2 :class="{'active': isActive}">hello</h2>用法二,传入多个值
<h2 :class="{'active': isActive, 'line': isLine}">hello</h2>用法三:
<h2 class="title" :class="{'active': isActive}"></h2>用法四:
可以放在一个methods或者computed中
<h2 class="title" :class="classes">hello</h2>

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

<div id="app"><h2 class="title" :class="[active, line]">{{mesg}}</h2><h2 class="title" :class="getClasses()">{{mesg}}</h2>
</div><script>const app = new Vue({el: '#app',data: {message: 'hello',active: 'aaa',line: 'bbb',},methods: {getClasses: function() {return [this.active, this.line]}}})
</script>

v-bind动态绑定style

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

绑定方法:对象语法:

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

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

绑定方法:数组语法:

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

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

计算属性的基本属性

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

<div id="app"><h2>{{firstName}}{{lastName}}</h2>
</div><script>const vm = new Vue({el: '#app',data: {firstName: 'web',lastName: 'it',}})
</script>
<div id="app"><h2>{{fullName}}</h2>
</div><script>const vm = new Vue({el: '#app',data: {firstName: 'jeskson',lastName: 'it',},computed: {fullName() {return this.firstName + ' ' + this.lastName}}})
</script>

计算属性的缓存:

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

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

setter和getter

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

<div id="app"><div>{{fullName}}</div><div>{{firstName}}</div><div>{{lastName}}</div>
</div><script>let vm = new Vue({el: '#app',data: {firstName: 'web',lastName: 'it',},computed: {fullName: {get() {rturn this.firstName+" "+this.lastName},set(newValue){const names = newValue.split(' ')this.firstName = names[0]this.lastName = names[1]}}}})
</script>
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<btns.length; i++) {(function(i){btns[i].onclick = function(){alert('点击了'+i+"个")}})(i)
}
let btns = document.getElementsByTagName('button');
for(let i=0;i<btns.length;i++){btns[i].onclick = function(){alert('点击了'+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<btns.length; i++) {btns[i].addEventListener('click', function(){console.log('第'+i+'个按钮被点击');})
}

闭包:

var btns = document.getElementsByTagName('button');
for(var i=0; i<btns.length;i++){(function(i){btns[i].addEventListener('click', function(){console.log('第'+i+'个按钮');  })})(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++"

<div id="app"><h2>点击次数:{{counter}}</h2><button v-on:click="counter++">按钮点击</button><button v-on:click="btnClick">按钮点击2</button>
</div>let app = new Vue({el: '#app',data: {counter: 0},methods: {btnClick(){this.counter++}}
})

v-on修饰符的使用

<div id="app"><div @click="divClick">web<button @click.stop="btnClick">按钮</button>
</div>

Vue提供了一些修饰符:

.stop 调用event.stopPropagation().prevent 调用event.preventDefault().native 监听组件根元素的原生事件.once 只触发一次回调
// 停止冒泡
<button @click.stop="doThis"></button>// 阻止默认行为
<button @click.prevent="doThis"></button>// 阻止默认行为,没有表达式
<form @submit.prevent></form>// 串联修饰符
<button @click.stop.prevent = "doThis"></button>// 键修饰符,键别名
<input @keyup.enter="onEnter">// 键修饰符,键代码
<input @keyup.13="onEnter">// 点击回调智慧触发一次
<button @click.once="doThis"></button>

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

简单使用:

<div id="app"><p v-if="score>=90">优秀</p><p v-else-if="score>=80">良好</p><p v-else-if="score>=60">及格</p><p v-else="score<60">不及格</p>
</div>

登录切换:

<div id="app"><span v-if="type==='username'"><label>用户账号:</label><input placeholder="用户账户"></span><span v-else><label>邮箱地址:</label><input placeholder="邮箱地址"></span><button @click="handleToggle">切换类型</button>
</div><script>let app = new Vue({el: '#app',data: {type: 'username'},methods: {handleToggle(){this.type = this.type === 'email' ? 'username' : 'email'}}})
</script>
<div id="app"><span v-if="isUser"><label for="username">用户账户</label><input type="text" id="username" placeholder="用户账户"></span><span v-else><label for="email">用户邮箱</label><input type="text" id="email" placeholder="用户邮箱"></span><button @click="isUser=!isUser">切换类型</button>
</div><script>
const app = new Vue({el: '#app',data: {isUser: true}
})
</script>

v-for遍历对象

<div id="app"><ul><li v-for="(value, key, index) in info">{{value}}-{{key}}-{{index}}</li></ul>
</div><script>let app = new Vue({el: '#app',data: {info: {name: 'web',age: 12,}}})
</script>

组件的Key属性

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

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

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

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

购物车

<div id="app">
<table>
<thead>
<tr>
<th></th>
<th>书籍名称</th>
<th>出版日期</th>
<th>价格</th>
<th>购买数量</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="item in books">
<td v-for="value in item">{{value}}</td>
</tr>
</tbody>
</table>
</div>

表单绑定v-model

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

<div id="app"><input type="text" v-model="message"><h2>{{message}}</h2>
</div>

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的使用以及原理

<input type="text" :value="message" @input="message = $event.target.value"><script>const app = new Vue({el: '#app',data: {message: '你好啊'},methods: {valueChange(event){this.message = event.target.value;}}})
</script>

v-model是语法糖,本质:

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

代码:

<input type="text" v-model="message"><input type="text" v-bind:value="message" v-on:input="message = $event.target.value">

v-model:checkbox

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

单个勾选框:

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

多个复选框:

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

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

<div id="app"><label for="check"><input type="checkbox" v-model="checked" id="check">同意协议</label><label><input type="checkbox" v-model="hobbies" value="篮球">篮球</label><label><input type="checkbox" v-model="hobbies" value="台球">台球</label>
</div>

v-model:select

select分单选和多选两种情况

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

v-model结合select类型

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

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

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

// 选择一个值
<select v-model="mySelect"><option value="web">web</option><option value="it">it</option>
</select>
<p>您最喜欢的{{mySelect}}</p>// 选择多个值
<select v-model="mySelects" multiple><option value="web">web</option><option value="it">it</option>
</select>
<p>您最喜欢的{{mySelects}}</p>

input中的值绑定

<label v-for="item in origin"><input type="checkbox" :value="item" v-model="hobbies">{{item}}
</label>

修饰符

lazy修饰符:

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

number修饰符:

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

trim修饰符:

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

示例:

<div id="app"><input type="text" v-model.lazy="message"><h2>{{message}}</h2>
</div>

什么是组件化

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

注册组件的基本步骤:

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

示例:

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

组件示例:

<div id="app">
<my-cpn></my-cpn>
</div><script src="../js/vue.js"></script>
<script>// 创建组件构造器const myComponent = Vue.extend({template: `<div><h2>组件标题</h2><p>组件段落</p></div>`});// 注册组件Vue.component('my-cpn',myComponent);
</script>

全局组件和局部组件

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

示例:

组件标题<div id="app"><my-cpn><my-cpn><div><my-cpn><my-cpn></div>
</div>

示例:

<div id="app1"><my-cpn></my-cpn>
</div>
<div id="app2"><my-cpn></my-cpn>
</div><script src="../js/vue.js"></script>
<script>
// 创建组件构造器
const myComponent = Vue.extend({template: `<div><h2>web</h2></div>`
})
// 注册组件
Vue.component('my-cpn',myComponent);
let app1 = new Vue({el: '#app1'
})
let app2 = new Vue({el: '#app2'
})
<div id="app1"><my-cpn></my-cpn>
</div>
<div id="app2"> // 没有被渲染出来<my-cpn></my-cpn>
</div>
<script src="../js/vue.js"></script>
<script>
// 创建组件构造器
const myComponent = Vue.extend({template: `<div><h2>web</h2></div>`})let app1=new Vue({el: '#app1',components: {'my-cpn': myComponent}})let app2 = new Vue({el: '#app2'})
</script>

父组件和子组件

组件树

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

示例:

<div id="app"><parent-cpn></parent-cpn>
</div>
<script src="../js/vue.js"></script>
<script>// 创建一个子组件构造器const childComponent = Vue.extend({template: `<div>我是子组件的内容</div>`})// 创建一个父组件的构造器const parentComponent = Vue.extend({template: `<div>我是父组件的内容<child-cpn></child-cpn></div>`components: {'child-cpn': childComponent}})let app = new Vue({el: '#app',components: {'parent-cpn': parentComponent}})

注册组件的语法糖

示例:全局组件

<div id="app"><cpn1></cpn1>
</div>
<script>// 全局组件注册的语法糖// 注册组件Vue.component('cpn1', {template: `<div><h2>web</h2></div>`})const app = new Vue({el: '#app',data: {message: 'web',}})
</script>
<div id="app"><cpn2></cpn2>
</div>
// 注册局部组件的语法糖
const app = new Vue({el: '#app',data: {message: 'web'},components: {'cpn2': {template: `<div><h1>web</h1></div>`}}
})
</script>

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

组件模板抽离的写法

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

  1. 使用<script>标签
  2. 使用<template>标签

示例:

<div id="app"><my-cpn></my-cpn>
</div><script type="text/x-template" id="myCpn"><div><h2>web</h2></div>
</script><script src="../js/vue.js"></script>
<script>let app = new Vue({el: '#app',components: {'my-cpn': {template: '#myCpn'}}})
</script>

template标签

<template id="cpn"><div><h2>web</h2></div>
</template>
// 注册一个全局组件
Vue.component('cpn', {template: '#cpn'
})

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

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

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

<div id="app"><my-cpn></my-cpn>
</div><template id="myCpn"><div>{{message}}</div>
</template>
<script src="..."></script>
<script>
let app = new Vue({el: '#app',components: {'my-cpn': {template: 'myCpn',data() {return{message: 'web'}}}}
})

父子通信-父传子props

如何进行父子组件间的通信呢?

  1. 通过props向子组件传递数据
  2. 通过事件向父组件发送消息

props基本用法

在组件中,使用props来声明从父级接收到的数据

props的值:

  1. 字符串数组,数组中的字符串就是传递时的名称。
  2. 对象,对象可以设置传递时的类型,也可以设置默认值等。

camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 命名:

Vue.component('blog-post', {// 在 JavaScript 中是 camelCase 的props: ['postTitle'],template: '<h3>{{ postTitle }}</h3>'
})
<!-- 在 HTML 中是 kebab-case 的 -->
<blog-post post-title="hello!"></blog-post>

重申一次,如果你使用字符串模板,那么这个限制就不存在了。

prop 各自的名称和类型:

props: {title: String,likes: Number,isPublished: Boolean,commentIds: Array,author: Object,callback: Function,contactsPromise: Promise // or any other constructor
}
<!-- 动态赋予一个变量的值 -->
<blog-post v-bind:title="post.title"></blog-post><!-- 动态赋予一个复杂表达式的值 -->
<blog-postv-bind:title="post.title + ' by ' + post.author.name"
></blog-post><!-- 即便 `42` 是静态的,我们仍然需要 `v-bind` 来告诉 Vue -->
<!-- 这是一个 JavaScript 表达式而不是一个字符串。-->
<blog-post v-bind:likes="42"></blog-post><!-- 用一个变量进行动态赋值。-->
<blog-post v-bind:likes="post.likes"></blog-post>

传入一个对象的所有属性

如果你想要将一个对象的所有属性都作为 prop 传入,你可以使用不带参数的 v-bind (取代 v-bind:prop-name)

post: {id: 1,title: 'My Journey with Vue'
}<blog-post v-bind="post"></blog-post><blog-postv-bind:id="post.id"v-bind:title="post.title"
></blog-post>

一个简单的props传递:

<div id="app"><child-cpn :message="message"></child-cpn>
</div><template id="childCpn"><div> 显示信息:{{message}}</div>
</template><script>
let app = new Vue({el: '#app',data: {message: 'hello'},components: {'child-cpn': {templte: '#childCpn',props: ['message']}}
})
</script>

Vue 中,父子组件的关系

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

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

props支持的数据类型:

StringNumberBooleanArrayObjectDateFunctionSymbol

示例:

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来监听子组件事件。

自定义事件代码:

<div id="app"><child-cpn @increment="changeTotal" @decrement="changeTotal"></child-cpn><h2>点击次数</h2>
</div><template id="childCpn"><div><button @click="increment">+1</button><button @click="decrement">-1</button></div>
</template>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状态。

示例:

<div id="app"><parent-cpn></parent-cpn>
</div>// 父组件template
<template id="parentCpn"><div><child-cpn1></child-cpn1><child-cpn2></child-cpn2><button @click="showChildCpn">显示所有子组件信息</button></div>
</template>// 子组件
<template id="childCpn1"><h2>我是子组件1</h2>
</template>
// 子组件
<template id="childCpn2"><h2>我是子组件2</h2>
</template>Vue.component('parent-cpn',{template: '#parentCpn',methods: {showChildCpn(){for(let i=0; i<this.$children.length; i++){console.log(this.$children[i].message)}}}
})

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

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

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

父子组件的访问方式$refs

$children的缺陷:

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

$refs的使用:

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

示例:

<child-cpn1 ref="child1"></child-cpn1>
<child-cpn2 ref="child2"></child-cpn2>
<button @click="show">通过refs访问子组件</button>show() {console.log(this.$refs.child1.message);console.log(this.$refs.child2.message);
}

看看一个.vue文件项目

<template><div class="xxx"><div class="xxx":class="{active: currentIndex === index}"@click="itemClick(index)"v-for="(item,index) in titles"><span>{{item}}</span></div></div>
</template><script>export default {name: 'xxx',props: {titles: {type: Array,default() {return []}}},data: function() {return {currentIndex: 0}},}
</script><style scoped>.xxx {xxx: xxx;}
</style>

三层部分:

slot插槽的使用

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

v-slot用法:

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

默认插槽

子组件:

// 子组件
<template>
<div class="content">
// 默认插槽
<content-box class="text">
<slot>默认值</slot>
<content-box>
</div>
</template>

slot基本使用

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

子组件定义一个插槽:

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

示例:

<div id="app"><my-cpn></my-cpn><my-cpn><p>web</p></my-cpn>
</div><template id="myCpn"><div><slot>我是谁</slot></div>
</template>
<script>
Vue.component('my-cpn',{template: '#myCpn'
})let app = new Vue({el: '#app'
})
</script>

使用具名插槽

  1. slot元素添加一个name属性
  2. <slot name="web"></slot>

示例:

<div id="app">// 没有任何内容<my-cpn></my-cpn>// 传入某个内容<my-cpn><span slot="left">left</span></my-cpn><my-cpn><span slot="left">left</span><span slot="center">center</span><span slot="right">right</span>
</div><template id="myCpn"><div><slot name="left">1</slot><slot name="center">2</slot><slot name="right">3</slot></div>
</template>
<script>Vue.component('my-cpn', {template: '#myCpn'})let app = new Vue({el: '#app'})
</script>

编译作用域

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 = trueobj.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() {// redconsole.log(this.a); // 看不见
}

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

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

export default {data() {return {a: 1}},beforeCreate() {console.log(this.a);},created() {// redconsole.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

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

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

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

场景:

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

keep-alive生命周期:

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

app.vue父组件:

<template><div><button @click="myBtn"> myBtn </button><keep-alive><range v-if="isShow"></range></keep-alive></div>
</template><script>import range from './components/range.vue'export default {data() {return {a: 1,isShow: true}},methods: {myBtn() {this.isShow = !this.isShow}},components: {range}}
</script>

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):valif(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).valif(newVal === value || (newVal !== newVal && value !==value)){return}if(process.env.NODE_ENV !== 'production' && customSetter){customSetter()}val = newValchildOb = !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 insertedswitch(method) {case 'push':case 'unshift':inserted = argsbreakcase 'splice':inserted = args.slice(2)break}if(inserted) ob.observerArray(inserted)// 对插入的数据再次进行观测ob.dep.notify()// 通知视图更新return result}
}observeArray(items: Array<any>) {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 webpack@3.6.0 -g

局部安装

npm install webpack@3.6.0 --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,developmententry: '' // 入口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,developmententry: '' // 入口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和filenameoutput:{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 = falsenew Vue({el: '#app',router,render: h => h(App)
})

使用vue-router的步骤

  1. 创建路由组件
  2. 配置路由映射:组件和路径映射关系
  3. 使用路由:通过<router-link><router-view>

代码:

组件components

// home
<template><div><h2>我是首页</h2></div>
</template><script>export default {name: 'Home'}
</script><style scoped>
</style>
<template><div><h2>我是关于</h2></div>
</template><script>export default {name: 'Aboout'}
</script><style scoped>
</style>

App.vue

<template><div id="app"><router-link to="/home">首页</router-link><router-link to="/about">关于</router-link><router-view></router-view></div>
</div><script>
export default {name: 'App'
}
</script><style>
</style>

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)
})

路由的偶然值和修改为history模式

创建router实例

代码:

router->index.js

import Vue from 'vue'
import VueRouter from 'vue-router'// 注入插件
Vue.use(VueRouter)// 定义路由
const routes = []// 创建router实例
const router = new VueRouter({routes
})// 导出router实例
export default router

main.js代码:

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

router->index.js

import Vue from 'vue'
import VueRouter from 'vue-router'import Home from '../components/home'
import About from '../components/about'// 注入插件
Vue.use(VueRouter)// 定义路由
const  routes = [{path: '/home',component: Home},{path: '/about',component: About}
]

使用App.vue代码

<template><div id="app"><router-link to="/home">首页</router-link><router-link to="/about">关于</router-link><router-view></router-view></div>
</template>
<script>
export default {name: 'App',components: {}
}
  1. <router-link>该标签是一个vue-router已经内置的组件,它会被渲染成一个<a>标签
  2. <router-view>该标签会根据当前的路径,动态渲染出不同的组件。
  3. 网页的其他内容,比如顶部的标题或导航,或者底部的一些版本信息等会和<router-view>处于同一个等级。
  4. 在路由切换时,切换的是<router-view>挂载的组件,其他内容不会发生改变。

路由的默认路径

默认情况下,进入网站的首页,<router-view>渲染首页的内容,但是默认没有显示首页组件,必须让用户点击才可以。

那么如何让路径默认跳转到首页,并且<router-view>渲染首页组件呢,只需要配置一个映射就可以:

const routes = [{path: '/',redirect: '/home'}
]

配置解析:在routes中又配置了一个映射,path配置的是根路径:/redirect是重定向,就是我们将根路径重定向到/home的路径下。

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

改变路径的方式:

  1. urlhash
  2. html5history
  3. 默认情况下,路径的改变使用的urlhash

使用html5history模式:

// 创建router实例
const router = new VueRouter({routes,mode: 'history'
})

router-link,使用了一个属性:to,用于指定跳转的路径。tag可以指定<router-link>之后渲染成什么组件。

replace属性不会留下history记录,指定replace的情况下,后退键返回不能返回到上一个页面中。

active-class属性,当<router-link>对应的路由匹配成功时,会自动给当前元素设置一个router-link-activeclass,设置active-class可以修改默认的名称。

const router = new VueRouter({routes,mode: 'history',linkActiveClass: 'active'
})

路由代码跳转

App.vue代码:

// app.vue
<template><div id="app"><button @click="linkToHome">首页</button><button @click="linkToAbout">关于</button><router-view></router-view></div>
</template><script>export default {name: 'App',methods: {linkToHome() {this.$router.push('/home')},linkToAbout() {this.$router.push('/about')}}}
</script>

<img :src="imgURL" alt=""><router-link :to="'/uer/' + userId"> 用户 </router-link><script>export default {name: 'User',computed: {userId() {return this.$route.params.userId}}}
</sript>

const Home = () => import('../components/Home')
const HomeNews = () => import('../components/HomeNews')
const HomeMessage = () => import('../components/HomeMessage'){path: '/home',component: Home,children: [{path: 'news',component: HomeNews},{path: 'news',component: HomeMessage}]
}
<router-link to = "/home/news">新闻</router-link>
<router-link to = "/home/message">信息</router-link>

默认选中:

传递参数的方式

传递参数主要有两种类型,paramsquery

params的类型:

  1. 配置路由方式:/router/:id
  2. 传递的方式:在path后面跟着对应的值
  3. 传递后形成的路径:/router/123

vue-router传递参数代码

<router-link :to="{path: '/profile'}">用户</router-link>

统一资源定位符

统一资源定位符,统一资源定位器,统一资源定位地址,Url地址等,网页地址。如同在网络上的门牌,是因特网上标准的资源的地址。

userClick() {this.$router.push('/user/' + this.userId)
}btnClick() {this.$router.push({path: '/user',query: {name: 'web',age: 12,height: 1.2}})
}

$route$router是有区别的

获取参数通过$route对象获取的,在使用vue-router的应用中,路由对象会被注入每个组件中,赋值为this.$route,并且当路由切换时,路由对象会被更新。

<template><div><p> {{$route.params}} </p></div>
</template>

query的类型:

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

$route$router是有区别的

const router = new VueRouter({routes,mode: 'history',linkActiveClass: 'active'
})

Vue.config.productionTip = falseVue.prototype.test = function() {console.log('test')
}Vue.prototype.name  = 'web'

$route$router是有区别的

  1. $routerVueRouter实例,想要导航到不同url,则使用$router.push方法。
  2. $route为当前router跳转对象里面可以获取name,path,query,params等。

vue-router全局导航

meta:元数据

router.beforeEach((to,from,next) => {// from 跳转到todocument.title = to.matched[0].meta.titleconsole.log(to);next()
})
// 后置钩子hook
router.afterEach((to,from) => {console.log();
})

导航守卫:导航表示路由正在发生改变。

vue-router提供的导航守卫主要用来通过跳转或取消的方式守卫导航。有多种机会植入路由导航过程中,全局的,单个路由独享的,或者组件级的。

全局守卫

可以使用router.beforeEach,注册一个全局前置守卫:

const router = new VueRouter({..})router.beforeEach((to,from,nex)=>{})

当一个导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫resolve完之前一直处于等待中。

  1. to:Route,即将要进入的目标路由对象
  2. from:Route,当前导航正要离开的路由
  3. next:Function,一定要调用该方法来resolve这个钩子。

vue-router-keep-alive

keep-alivevue-router

router-view是一个组件,如果直接被包含在keep-alive里面,所有路径匹配到的视图组件都会被缓存。

keep-aliveVue内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染。

属性:

  1. include字符串或正则表达式,只有匹配的组件会被缓存
  2. exclude字符串或正则表达式,任何匹配的组件都不会被缓存
<keep-alive><router-view>// 所有路径匹配到的视图组件都会被缓存</router-view>
<keep-alive>

Promise的使用

es6的特性Promise,它是异步编程的一种解决方案。

定时器的异步事件:

setTimeout(function() {let data = 'web'console.log(content)
},1000)new Promise((resolve, reject) => {setTimeout(function(){resolve('web')reject('error')},1000)
}).then(data=>{console.log(data)
}).catch(error=> {console.log(error)
})

Promise三种状态:

  1. pending等待状态,比如正在进行网络请求,或定时器没有到时间。
  2. fulfill,满足状态,主动回调resolve时,并且回调.then()
  3. reject,拒绝状态,回调reject时,并且回调.catch()

Vuex详解

vuex是一个专门为vue.js应用程序开发的状态管理模式

它采用集中式存储管理应用的所有组件的状态,,并以相应的规则保证状态以一种可预测的方式发生变化。

  1. 状态管理模式
  2. 集中式存储管理

View components -> actions(dispatch方式) -> mutations(commit方式) -> state -> View components

Vuex核心概念5个:

StateGettersMutationActionModule

State单一状态树,单一数据源。

Mutation状态更新

Vuexstore的更新唯一方式,提交Mutation

Mutation的主要包括两部分:

  1. 字符串的事件类型
  2. 一个回调函数,该回调函数的第一个参数就是state

mutation的定义:

mutations: {increment(state) {state.count++}
}

通过mutation更新

increment: function() {this.$store.commit('increment')
}

参数被称为是mutation的载荷payload

Vuexstore中的state是响应式的,当state中的数据发生改变时,Vue组件会自动更新。

  1. 提前在store中初始化好所需的属性
  2. state中的对象添加新属性时:使用
  • 使用Vue.set(obj,'newObj',123)
  • 用新对象给旧对象赋值

Mutation常量类型

// mutation-types.js
export const UPDATE_INFO = 'UPDATE_INFO'import Vuex from 'vuex'
import Vue from 'vue'
import * as types from './mutation-types'Vue.use(Vuex)const store = new Vuex.Store({state: {info: {name: 'web',age: 12}},mutations: {[types.UPDATE_INFO](state, payload) {state.info = {...state.info, 'height': payload.height}}
})
<script>import {UPDATE_INFO} from './store/mutation-types';export default{name: 'App',components: {},computed: {info(){return this.$store.state.info}},methods: {updateInfo(){this.$store.commit(UPDATE_INFO,{height:1.00})}}}
</script>

注意:不要再mutation中进行异步操作,mutation同步函数,在其中的方法必须时同步方法。

action的基本定义,如果有异步操作,比如网络请求,

// 不能再mutation中使用异步操作,不能再这里进行异步操作
update(state) {setTimeout(()=>{state.info.name = 'web'},1000)
}mutations: {// 方法[INCREMENT](state){state.counter++}
}
actions: {// context:上下文,=》store<!--aUpdateInfo(context) {--><!--    setTimeout(()=>{--><!--        state.info.name = 'web'--><!--    },1000)--><!--}-->
}
actions: {aUpdateInfo(context) {setTimeout(()=>{context.commit('updateInfo')},1000)}
}// xx.vue
updateInfo(){this.$store.dispatch('aUpdateInfo')
}

updateInfo(){<!--this.$store.commit('updateInfo')-->this.$store.dispatch('aUpdateInfo',{message: 'web',success: () => {console.log('web')}})
}
aUpdateInfo(context, payload) {return new Promise((resolve, reject) => {...})
}

vuex中的modules使用

modules时模块的意思

getters: {stu(){},stuLength(state, getters) {return getters.stu.length}
}

使用根数据:

getters: {fullName(state) {return state.name + '1'},fullName1(state, getters) {return getters.fullName + '2'},fullName3(state, getters, rootState) {return getters.fullName2+rootState.counter}
}

在模块中actions打印console.log(context)

actions接收一个context参数对象,局部状态通过context.state暴露出来,根节点状态为context.rootState

import mutations from './mutations'
import actions from './actions'
import getters from './getters'
import moduleA from './modules/moduleA'import Vuex from 'vuex'
import Vue from 'vue'
Vue.use(Vuex)const state = {}const store = new Vuex.Store({state,mutations,actions,getters,modules: {a: moduleA}
})export default store

网络封装

axios网络模块的封装

ajax是基于XMLHttpRequest(XHR)jQuery-Ajax相对于传统的ajax非常好用。

axios特点:

  • 在浏览器中发送XMLHttpRequests请求
  • node.js中发送http请求
  • 支持Promise API
  • 拦截请求和响应
  • 转换请求和响应数据

axios请求方式:

axios(config)
axios.request(config)
axios.get()
axios.delete()
axios.head()
axios.post()
axios.put()
axios.patch()

安装

npm install axios --save
axios({// 默认geturl: '',method: 'get'
}).then(res=>{console.log(res)
})
// import request from "../utils/request.js"
import {request} from './network'export function getHome() {return request({url: '/home/xxx'})
}export function getXX(type, page) {return request({url: '/home/xx',params: {type,page}})
}

并发请求

代码:

axios.all([axios({url: ''
}), axios({url: '',params: {type: '',page: 1,}
})]).then(results => {})// then(axios.spread((res1,res2)=>{...}))

全局配置

axios.defaults.baseURL=''axios.all ..{url: '/home'
}axios.defaults.baseURL = 'https://api.example.com';
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';axios.defaults.baseURL = global.HOST;

request.js

import axios from 'axios'
export function request(config,success,failure){// 创建axios实例const instance = axios.create({baseURL: '',timeout: 5000})// 发送网络请求instance(config).then(res=>{success(res)}).catch(err=>{failure(err)})
}

main.js

import {request} from './xx/request'request({url: ''
},res=>{),err=>{}

也可以使用promise方法,不过本身返回的就是promise

import axios from 'axios'
export function request(config) {const instance = axios.create({baseURL: '',timeout: 2000})return instance(config)
}

axios拦截器的使用

// 配置请求和响应拦截
instance.interceptors.request.use(config => {console.log('request拦截success中')return config
},err => {console.log('request拦截failure中')return err
})instance.interceptors.response.use(response => {console.log('response拦截success中')return response.data
},err => {console.log('response拦截failure中')return err
})

封装axios

// request.js
import axios from 'axios'
cosnt service = axios.create({baseURL: process.env.BASE_API,timeout: 2000
})service.interceptors.request.use(config=>{//发请求前做的一些处理,数据转化,配置请求头,设置token,设置loading等config.data=JSON.stringify(config.data);config.headers = {'Content-Type':'application/x-www-form-urlencoded'}return config
},error=>{Promise.reject(error)
})// 响应拦截器
service.interceptors.response.use(response => {return response
}, error => {if (error && error.response) {switch (error.response.status) {case 400:error.message = '错误请求'break;case 401:error.message = '未授权,请重新登录'break;case 403:error.message = '拒绝访问'break;case 404:error.message = '请求错误,未找到该资源'window.location.href = "/NotFound"break;case 405:error.message = '请求方法未允许'break;case 408:error.message = '请求超时'break;case 500:error.message = '服务器端出错'break;case 501:error.message = '网络未实现'break;case 502:error.message = '网络错误'break;case 503:error.message = '服务不可用'break;case 504:error.message = '网络超时'break;case 505:error.message = 'http版本不支持该请求'break;default:error.message = `连接错误${error.response.status}`}} else {if (JSON.stringify(error).includes('timeout')) {Message.error('服务器响应超时,请刷新当前页')}error.message('连接服务器失败')}Message.error(err.message)return Promise.resolve(error.response)
})
// 导入文件
export default service

封装请求http.js

import request from './request'
const http ={/*** methods: 请求* @param url 请求地址 * @param params 请求参数*/get(url,params){const config = {methods: 'get',url:url}if(params){config.params = params}return request(config)},post(url,params){const config = {methods: 'post',url:url}if(params){config.data = params}return request(config)},put(url,params){const config = {methods: 'put',url:url}if(params){config.params = params}return request(config)},delete(url,params){const config = {methods: 'delete',url:url}if(params) {config.params = params}return request(config)}
}export default http
// api.js
import http from '../utils/http'
let resquest = "/xx/request/"
// get请求
export function getListAPI(params){return http.get(`${resquest}/getList.json`,params)
}// js//创建新的axios实例,
const service = axios.create({baseURL: process.env.BASE_API,timeout: 3 * 1000
})

项目

创建项目:

vue create webMallnpm run serve

// .editorconfig
root = true
[*]
charset = utf-8
indent_style=space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

项目在window下部署

main.js代码:

import store from './store'
import FastClick from 'fastclick'
import VueLazyLoad from 'vue-lazyload'import toast from 'components/common/toast'Vue.config.productionTip = false
// 添加事件总线对象
Vue.prototype.$bus = new Vue()
// 安装toast插件
Vue.use(toast)
// 解决移动端300ms延迟
FastClick.attach(document.body)
// 使用懒加载的插件
Vue.use(VueLazyLoad,{loading: require('./xx.png')
})

windows安装nginxlinux部署,centos上安装nginx

linux ubuntu

Ubuntu是一个以桌面应用为主的Linux操作系统,其名称来自非洲南部祖鲁语或豪萨语的“ubuntu"一词。

操作系统:Window10 + Centos6.5(虚拟机)

yum install nginx
systemtl start nginx.service
systemctl enable nginx.service

通过Xftp将vue项目文件上传至云服务器

使用Xshell连接云服务器

主机就是阿里云上创建的实例的公网ip

输入登录名和密码,登录名就是购买服务器时输入的登录名和密码。

运行npm run build命令,有一个dist文件夹,这就是vue项目打包后的文件。

nginx安装配置

Xshell终端输入命令yum install nginx,当需要确认时输入”y“回车。

安装完成后,输入service nginx start启动nginx服务。

通过命令nginx -t查看nginx所在的安装目录。

在命令行输入命令cd/etc/nginx 切换到nginx目录下,再输入cat nginx.conf可查看当前nginx配置文件。

输入命令 wget https://nodejs.org/dist/v10.8.0/node-v10.8.0-linux-x64.tar.xz 回车,等待安装。

输入命令tar xvf node-v10.8.0-linux-x64.tar.xz 回车进行解压操作。

小结:

  1. 计算属性在多次使用时,只会调用一次,因为它是有缓存额
  2. 修饰符:stopprevent.enter.once.native等,lazynumbertrim等。
  3. 模板的分类写法:scripttemplate
  4. 父子组件的通信:父传子,props,子传父,$emit
  5. 项目,npm installnpm run serve
  6. webStorm开发vuePlugins安装插件vue.js
  7. 2.6.0 版本中,Vue为具名插槽和作用域插槽引入了一个新的统一的语法 (即 <v-slot> 指令)。它取代了 slotslot-scope 这两个目前已被废弃、尚未移除,仍在文档中的特性。
  8. v-slot 用法,分为三类:默认插槽、具名插槽以及作用域插槽。

作用域插槽,通过 slot-scope属性来接受子组件传入的属性集合

  • 默认插槽

代码:

// 子组件
<template><div><header><slot>默认值</slot></header></div>
</template>

任何没有被包裹在带有v-slot<template>中的内容都会被视为默认插槽的内容。当子组件只有默认插槽时,<v-slot>标签可以直接用在组件上

// 父组件
<template><div><child>内容1<template>内容2</template>内容3</child><child v-slot="web">插槽<br>插槽<br></child></div>
</template>
  • 具名插槽:v-slot 重复定义同样的 name 后只会加载最后一个定义的插槽内容
// 子组件
<template><div><main><slot name="main"></slot></main><footer><slot name="footer"></slot></footer></div>
</template>
  • 作用域插槽:
// 子组件
<template><div><footer><slot name="footer" :user="user" :testBtn="testBtn">{{user.name}}</slot></footer></div>
</template><script>exportdefault {name: 'child',data () {return {user: {title: 'web',name: 'web'}};},methods:{testBtn(){alert('web');}}};</script>

Vue如何直接调用Component里的方法

<template><div><b-component ref="BComponent"></b-component></div>
</template><script>
import BComponent from './BComponent'export default {name: 'A',data () {},components: {BComponent},methods: {callACompoentFunction () {this.$refs.BComponent.sayHi()}}
}
</script><style scoped>
</style>
<template><div></div>
</template><script>export default {name: 'B',data () {},methods: {sayHi () {console.log('web!')}}
}
</script><style scoped>
</style>

最后

欢迎关注我的CSDN,关注不迷路。点赞评论加收藏!

【图文并茂,点赞收藏哦!】重学巩固你的Vuejs知识体系相关推荐

  1. 重学巩固你的Vuejs知识体系(上)

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

  2. (连载)边喝咖啡边学Unity——第二章 预备知识体系

    第二章 预备知识体系 --本章涉及空间数学.解析几何.线性代数.计算机图形学.算法.数据结构等众多基础学科.同上一章相比,虽然枯燥,但是绝不能称为废话之章,即使粗略的看一遍,也比直接跳过来的好,详细地 ...

  3. 《JavaScript》重学JS-细聊一下prototype、__proto__与constructor(超详解版)

    求关注,求收藏,求点赞,非常感谢!你的每一个阅读都是我的力量源泉! 前言 最近在比对class以及将class编译成es5后的代码,看的是晕头转向,尤其在原型链这一块,发现之前的了解实在有些不够,因此 ...

  4. css html 双面打印_CSS语法与规则 — 重学CSS

    我是三钻,一个在<技术银河>中等你们一起来终生漂泊学习. 点赞是力量,关注是认可,评论是关爱!下期再见 ! 前言 进入重学 CSS 的第一步,首先需要找到一些线索.我们在前面的课程中讲学习 ...

  5. 【点赞收藏】36个顶级数据分析方法与模型!

    来源:数据分析不是个事儿 好的数据分析师不仅熟练地掌握了分析工具,还掌握了大量的数据分析方法和模型. 这样得出的结论不仅具备条理性和逻辑性,而且还更具备结构化和体系化,并保证分析结果的有效性和准确性. ...

  6. 打分五角星html制作,css简单实现五角星评分、点赞收藏、展示评分(半颗星、1/3颗星)...

    css简单实现五角星评分.点赞收藏.展示评分(半颗星.1/3颗星) 1. 前言 之前做的好几个项目中,都会遇到打分,评分,点赞这样的需求,写了很多次,每次需要再写的时候,就会翻出之前写过的代码,然后 ...

  7. 《重学设计模式》PDF 出炉了 - 小傅哥,肝了50天写出18万字271页的实战编程资料...

    持续坚持原创输出,点击蓝字关注我吧 作者:小傅哥 博客:https://bugstack.cn ❝ 沉淀.分享.成长,让自己和他人都能有所收获!???? ❞ 目录 一.前言 二.简介 1. 谁发明了设 ...

  8. 《重学 Java 设计模式》PDF 出炉了 - 小傅哥,肝了50天写出18万字271页的实战编程资料...

    持续坚持原创输出,点击蓝字关注我吧 作者:小傅哥 博客:https://bugstack.cn ❝ 沉淀.分享.成长,让自己和他人都能有所收获!???? ❞ 目录 一.前言 二.简介 1. 谁发明了设 ...

  9. 重学Java(四):操作符

    之前我写了一篇<重学Java(四):对象.引用.堆.栈.堆栈>,本以为凭借自己8年的Java编程经验足够把这些"吃人"的Java名词解释清楚了,但有网友不以为然,在文章 ...

最新文章

  1. 使用GoAccess分析Nginx日志
  2. linux网卡绑定脚本
  3. 简单小练习_自动收集文件到文件夹
  4. QT开发(十二)——QT事件处理机制
  5. 西工大与东北大学计算机,国内世界高水平大学排名:西北工业大学位居第一,东北大学排第二...
  6. Adding Applications for JavaScript Storefronts in SAP 电商云
  7. 必读的10篇关于GAN的论文
  8. G - Hard problem CodeForces - 706C DP
  9. linux下redis安装配置及redis常用命令(实战详细版)
  10. 人工智能 21 个子领域高被引学者Top 3
  11. 7-3 素数对猜想 (40 分)
  12. Javascript下拉刷新
  13. [译]GLUT教程 - 整合代码2
  14. PHP探测手机客户端
  15. python计算均方误差_python 计算平均平方误差(MSE)的实例
  16. Mybatis XML文件属性配置
  17. 如果判断小米MIUI的NFC权限、后台弹出界面权限是否禁用
  18. 了解黑客经常使用哪些工具
  19. Viruses!!!!!
  20. [MFC] CList

热门文章

  1. sqlLite数据库 实现对数据的增删改查 以及用list显示出数据内容
  2. 你不得不了解的语义分割发展史
  3. MultipeerConnectivity.framework梳理
  4. The import xxx cannot be resolved
  5. 微信小程序嵌入Udesk即时通讯网页插件
  6. 【地球科学】DEM数据简要总结及ALOS PALSAR12.5m数据获取和对比显示
  7. yaml文件格式说明
  8. 基于VHDL语言八位加法器设计
  9. 宠物乐园项目简介_苏州 景点门票/儿童乐园特价
  10. 探索科技手段下的食品安全,区块链冷链追溯平台建设解决方案