写点什么

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

用户头像
魔王哪吒
关注
发布于: 2021 年 01 月 27 日
【图文并茂,点赞收藏哦!】重学巩固你的Vuejs知识体系

哪吒人生信条:如果你所学的东西 处于喜欢 才会有强大的动力支撑。



每天学习编程,让你离梦想更新一步,感谢不负每一份热爱编程的程序员,不论知识点多么奇葩,和我一起,让那一颗像四处流荡的心定下来,一直走下去,加油,2021加油!欢迎关注,欢迎点赞、收藏和评论



不要害怕做梦,但是呢,也不要光做梦,要做一个实干家,而不是空谈家,求真力行。前沿


置身世外只为暗中观察!!!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 -> itconsole.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 = 12let 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-post v-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-post v-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支持的数据类型:


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


自定义事件代码:


<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 = 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


<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):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<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,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.jsmodule: {    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.jsmodule.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 buildruntime

  6. Install vue-routerno是否安装等


目录结构详解


buildconfigwebpack相关配置,node_modules 是依赖的node相关的模块,src是写代码地方。 .babelrc是 es 代码相关转换配置,.editorconfig项目文本相关配置,.gitignoregit仓库忽略的文件夹配置,.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 中的 hash 和 html5 的 history


前端路由的核心是改变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. 使用路由:通过<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 = falsenew 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.jsconst 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 = false
Vue.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 跳转到to    document.title = to.matched[0].meta.title    console.log(to);    next()})
复制代码


// 后置钩子hookrouter.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.jsexport 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.vueupdateInfo(){ 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({    // 默认get    url: '',    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.jsimport 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.jsimport 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 webMall
npm run serve
复制代码



// .editorconfigroot = true[*]charset = utf-8indent_style=spaceindent_size = 2end_of_line = lfinsert_final_newline = truetrim_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 nginxsystemtl start nginx.servicesystemctl 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>
复制代码


点关注,不迷路



愿你遇到那个心疼你付出的人~



面向 JavaScript 爱好人员提供:Web 前端最新资讯、原创内容、JavaScript、HTML5、Ajax、jQuery、Node.js、Vue.js、React、Angular 等一系列教程和经验分享。

点赞、收藏和评论



我是 Jeskson(达达前端),感谢各位人才的:点赞、收藏和评论,我们下期见!(如本文内容有地方讲解有误,欢迎指出☞谢谢,一起学习了)



我们下期见!



文章持续更新,可以微信搜一搜「 程序员哆啦 A 梦 」第一时间阅读,回复【资料】有我准备的一线大厂资料,本文 http://www.dadaqianduan.cn/#/ 已经收录



github 收录,欢迎 Star:https://github.com/webVueBlog/WebFamily

发布于: 2021 年 01 月 27 日阅读数: 956
用户头像

魔王哪吒

关注

微信搜:程序员哆啦A梦 2018.05.08 加入

面向JavaScript爱好人员提供:Web前端最新资讯、原创内容、JavaScript、HTML5、Ajax、jQuery、Node.js、Vue.js、React、Angular等一系列教程和经验分享。 博客首发:http://www.dadaqianduan.cn/#/

评论 (1 条评论)

发布
用户头像
可以搞成系列了
2021 年 01 月 28 日 10:40
回复
没有更多了
【图文并茂,点赞收藏哦!】重学巩固你的Vuejs知识体系