内容简介:置身世外只为暗中观察!!!Hello大家好,我是魔王哪吒!重学巩固你的Vuejs知识体系,如果有哪些知识点遗漏,还望在评论中说明,让我可以及时更新本篇内容知识体系。欢迎点赞收藏!
前沿
置身世外只为暗中观察!!!Hello大家好,我是魔王哪吒!重学巩固你的Vuejs知识体系,如果有哪些知识点遗漏,还望在评论中说明,让我可以及时更新本篇内容知识体系。欢迎点赞收藏!
谈谈你对MVC、MVP和MVVM的理解?
https://github.com/webVueBlog...
转角遇到Vuejs
- 你为啥学习Vuejs
- 前端开发的复杂化
- Vuejs的特点
- 安装Vuejs
- 体验Vuejs
- MVVM架构:data和Vue对象的分离,Vue中的MVVM
目录:
起步
- 插值语法:Mustache,v-once,v-html,v-text,v-pre,v-block。
- 绑定属性:v-bind的介绍,v-bind的基础,v-bind的语法糖,绑定class,绑定样式。
- 计算属性
- 事件监听:v-on介绍,v-on基础,v-on参数,v-on修饰符
- 条件和循环:条件渲染,v-show指令,v-if和v-show对比
- 表单绑定:基本使用,v-model原理,其他类型,值绑定,修饰符。
组件化开发:
什么是组件化,Vue组件化开发思想
- 注册的步骤
- 全局和局部组件
- 父组件和子组件
- 注册组件语法糖
- 模板的分离写法
- 组件的其他属性
- 父级向子级传递
- 子级向父级传递
- 父子组件的访问
- 非父子组件通信
组件化高级语法:
- 插槽slot:编译作用域,为什么使用slot,slot的基本使用,slot的具名插槽,slot的作用域插槽。
- 动态组件
- 异步组件
- 组件声明周期
Vue Cli
- 什么是webpack
- webpack和gulp对比
- 手动webpack的配置
- Vue Cli是什么
- Vue Cli依赖环境
- Vue Cli的安装
网络封装
- 使用传统的Ajax是基于XMLHttpRequest(XHR)
- 使用jQuery-Ajax
- Vue-resource
- 使用axios
axios的使用
- 了解axios:axios请求方式
- 发送请求,发送get请求,发送并发请求,axios全局配置,常见配置选项。
- axios实例,为什么创建axios实例,如何创建axios实例,axios的封装。
- axios的拦截器:请求和响应
vuejs原理相关:响应式原理,源码。
vue.js是什么
- vue是一套用于构建用户界面的渐进式框架。
- 从自底向上逐层应用,核心库是只关注图层。
- 易于学习,便于与第三方库或既有项目整合。
Vue基础语法
对于基础知识需要掌握,简单写写✍
vue.js安装
直接CDN引入:
- 对于制作原型或学习
代码: <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
- 对于生产环境
代码: <script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
- 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>
小案例-计算器
- 新的属性:
methods
,该属性是用于Vue
对象中定义的方法。 - 新的指令:
@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的思想
- view是我们的DOM
- Model是我们抽离出来的obj
- ViewModel是我们创建的Vue对象实例
它们之间是如何工作的呢?
- ViewModel通过Data Binding让obj中的数据实时在DOM中显示
- ViewModel通过DOM Listener来监听DOM事件,并且通过methods中的操作,来改变obj中的数据
-
el
:类型:string | HTMLElement
- 作用:决定之后Vue实例会管理哪一个
DOM
-
data
:类型:Object | Function
- 作用:Vue实例对应的数据对象
-
methods
:类型:{[key:string]:Function}
- 作用:定义属于Vue的一些方法,可以在其他地方调用,也可以在指令中使用。
什么是Vue的生命周期
生命周期:☞ 事物从诞生到消亡的整个过程
release debug
- Mustache语法也就是双大括号
- 插值操作
- 绑定属性
- 计算属性
- 事件判断
- 循环遍历
- 阶段案例
- v-model
v-once
指令的使用
<div id="app"> <h1>{{message}}</h1> <h2 v-once>{{message}}</h2> </div>
v-once
:
- 该指令后面不需要跟任何表达式
- 该指令表示元素和组件只渲染一次,不会随着数据的改变而改变
v-html
:
当我们从服务器请求到的数据本身就是一个HTML代码时
- 如果直接通过
{{}}
来输出,会将HTML
格式进行解析,并且显示对应的内容。 - 可以使用
v-html
指令 - 该指令后跟上一个
string
类型 - 会将
string
的html
解析处理并且进行渲染
<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>{{message}}</h2> </div> <script> const app = new Vue({ el: '#app', data: { message: 'hello' } }) </script>
绑定 class
有两种方式:
- 对象语法
- 数组语法
对象语法:
用法一:直接通过{}绑定一个类 <h2 :class="{'active': isActive}">hello</h2> 用法二,传入多个值 <h2 :class="{'active': isActive, 'line': isLine}">hello</h2> 用法三: <h2 :class="{'active': isActive}"></h2> 用法四: 可以放在一个methods或者computed中 <h2 :class="classes">hello</h2>
v-bind
动态绑定class,数组语法
<div id="app"> <h2 :class="[active, line]">{{mesg}}</h2> <h2 :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
后面跟的是一个对象类型,对象的 key
是 css
属性名称,对象的 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
是语法糖,本质:
-
v-bind
绑定一个value
属性 -
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
即为布尔值。 input
的 value
并不影响 v-model
的值。
多个复选框:
当是多个复选框时,对应的 data
中属性是一个数组。
当选中某一个时,就会将 input
的 value
添加到数组中。
<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
修饰符:
- 默认情况下,
v-model
默认是在input
事件中同步输入框的数据的。 - 一旦有数据发生改变对应的
data
中的数据就会自动发生改变。 -
lazy
修饰符可以让数据在失去焦点或者回车时才会更新。
number
修饰符:
number
trim
修饰符:
trim
示例:
<div id="app"> <input type="text" v-model.lazy="message"> <h2>{{message}}</h2> </div>
什么是组件化
- 组件化是vue.js中的重要思想
- 它提供了一种抽象,让我们可以开发出一个个独立可复用的小组件来构造我们的应用
- 任何的应用都会被抽象成一颗组件树
注册组件的基本步骤:
- 创建组件构造器
- 注册组件
- 使用组件
示例:
调用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>
全局组件和局部组件
-
Vue.extend()
调用Vue.extend()
创建一个组件构造器。 - 通常在创建组件构造器时,传入
template
代表我们自定义组件的模板。 - 该模板在使用到组件的地方,显示的
html
代码。 - 这种写法在
Vue2.x
的文档几乎看不到了。 -
Vue.component()
是将刚才的组件构造器注册为一个组件,并且给它起一个组件的标签名称。 - 注册组件的标签名,组件构造器。
示例:
组件标题 <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>
父组件和子组件
组件树
- 组件和组件之间存在层级关系
- 其中一种非常重要的关系就是父子组件的关系
示例:
<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
模块内容:
<script> <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
如何进行父子组件间的通信呢?
props
props
基本用法
在组件中,使用 props
来声明从父级接收到的数据
props
的值:
- 字符串数组,数组中的字符串就是传递时的名称。
- 对象,对象可以设置传递时的类型,也可以设置默认值等。
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
用于父组件向子组件传递数据,还有一种比较常见的是子组件传递数据或事件到父组件中。
自定义事件:
$emit() 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
有时候需要父组件直接访问子组件,子组件直接访问父组件,或者是子组件访问父组件。
- 父组件访问子组件,使用
$children
或者$refs
- 子组件访问父组件,使用
$parent
对于 $children
的访问:
this.$children 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
$parent
父子组件的访问方式 $refs
$children
的缺陷:
$children $refs
$refs
的使用:
-
$refs
和ref
指令通常一起使用 - 通过
ref
给某个子组件绑定一个特定的id
- 通过
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> <div :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
用法:
- 默认插槽
- 具名插槽
- 作用域插槽
-
slot
以及slot-scope
的用法:子组件编写,父组件编写
默认插槽
子组件:
// 子组件 <template> <div> // 默认插槽 <content-box> <slot>默认值</slot> <content-box> </div> </template>
slot
基本使用
<slot>
子组件定义一个插槽:
-
<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>
使用具名插槽
- 给
slot
元素添加一个name
属性 -
<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模块化编程
- 可以解决项目中的全局变量污染问题
- 开发效率高,利于多人协同开发
- 职责单一,方便代码复用和维护
- 解决了文件的依赖问题
那么什么是模块化呢
将一个项目按照功能划分,理论上一个功能一个模块,互不影响,在需要的时候载入,尽量遵循高内聚低耦合。
了解 CommonJS
CommonJS 是一种思想,本质上是可复用的JavaScript,它导出特定的对象,提供其它程序使用。
使用 module.exports
和 exports.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文件又很多,就会出现一些问题:
- 变量名冲突
- 文件依赖复杂度高
- 页面加载过多,不利于维护
CommonJS
,定义模块,一个单独的 js
文件就是一个模块,每个模块都有自己单独的作用域,在该模块内部定义的变量,无法被其他模块读取,除了定义为 global
对象的属性。
模块的导出: exports
和 module.exports
模块的导入: require
- 在
node
中,每个模块内部都有要给自己的module
对象 - 在
module
对象中,有一个成员exports
也是一个对象 - 通过
exports
对象导出当前方法或变量,也可通过module.exports
导出 -
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
属性。
实例化期和加载期
创建期间的生命周期函数: beforeCreate
和 created
, beforeMount
和 mounted
。
beforeCreate
在实例初始化后,数据观测 data observer
和 event/watcher
事件配置之前被调用。
更新期
运行期间的生命周期函数: beforeUpdate
和 updated
created
实例已经创建完成后被调用。
实例已完成以下的配置:数据观测 data observer
,属性和方法的运算, watch/event
事件回调。
挂载阶段还没开始, $el
属性目前不可见。
beforeMount
在挂载开始之前被调用,相关的 render
函数首次被调用。 mounted
, vm.$el
已经挂载在文档内,对已有 dom
节点的操作可以在期间进行。 beforeUpdate
数据更新时调用,发生在虚拟 dmo
重新渲染和打补丁之前。 updated
当这个钩子被调用时,组件 dom
已经更新,所以你现在可以执行依赖于 dom
的操作。 activated
, deactivated
, beforeDestroy
, destroyed
。实例销毁之前调用, vue
实例销毁后调用。
卸载期
销毁期间的生命周期函数: beforeDestroy
和 destroyed
实例生命周期钩子
每个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
activated
和 deactivated
(组件)
activated() { console.log("组件使用了"); }, deactivated() { console.log("组件停用了"); Data to Drag},
keep-alive
是 vue
的内置组件,能在组件切换过程中将状态保留在内存中,防止重复渲染 dom
。
<keep-alive>
包裹动态组件时,会缓存不活动的组件实例,而不会销毁它们。和 <transition>
相似, <keep-alive>
是一个抽象组件:它自身不会渲染一个 DOM
元素,也不会出现在父组件链中。
当组件在 <keep-alive>
内被切换,它的 activated
和 deactivated
这两个生命周期钩子函数将会被对应指定。
它的使用是因为我们不希望组件被重新渲染而影响使用体验,或者是性能,避免多次渲染降低性能。缓存下来,维持当前得状态。
场景:
- 商品列表页点击商品跳转到商品详情,返回后仍显示原有信息
- 订单列表跳转到订单详情,返回,等等场景。
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>
beforeDestroy
和 destroyed
beeforeDestroy
类型为 function
,详细:实例销毁之前调用,在这一步,实例仍然完全可用。
该钩子在服务器端渲染期间不被调用。
destroyed
类型为 function
,详细: vue
实例销毁后调用,调用后, vue
实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。
该钩子在服务器端渲染期间不被调用。
beforeRouteEnter
和 beforeRouteLeave
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
生命周期函数执行的时候, data
和 methods
中的数据都没有初始化。在 created
中, data
和 methods
都已经被初始化好了,如果要调用 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
树,重新渲染到真实的页面中,完成数据从 data
到 view
的跟新。
beforeDestroy
钩子函数执行时, vue
实例就从运行阶段,进入到了销毁阶段。此时的实例还是可用的阶段,没有真正执行销毁过程。 destroyed
函数执行时,组件已经被完全销毁了,都不可用了。
vue面试题
谈一谈你对 mvvm
的理解
双向绑定的过程
视图 view
,路由-控制器 Controller
,数据 Model
view
-> dom
, viewModel
, Model
数据
传统的 mvc
指用户操作会请求服务器端路由,路由会调用对应的控制器来处理,控制器会获取数据,将结果返回给前端,让页面重新渲染。
mvvm
,对于传统的前端会将数据手动渲染到页面上, mvvm
模式不需要用户收到操作 dom
元素,将数据绑定到 viewModel
层上,会自动将数据渲染到页面中,视图变化会通知 viewModel
层更新数据。
Vue响应式原理
-
vue
内部是如何监听message
数据的改变 - 当数据发生改变,
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
中的访问器属性中的 get
和 set
方法
- 把数据转化为
getter
和setter
,建立watcher
并收集依赖。
说明:
watcher
通过回调函数更新 view
; observer
观测 data
数据,通过 get
通知 dep
收集 watcher
, dep
通过 notify()
通知 watcher
数据更新, watcher
通过 addDep()
收集依赖。
Observer
:用于监听劫持所有 data
属性, dep,watcher,view
, Compile
解析 el
模板中的指令。
依照下图(参考《深入浅出 vue.js
》)
首先从初始化 data
数据开始,使用 Observer
监听数据,个体每个数据属性添加 Dep
,并且在 Data
,有两个 getter
, setter
。在它的 getter
过程添加收集依赖操作,在 setter
过程添加通知依赖的操作。
在解析指令或者给 vue
实例设置watch选项或者调用 $watch
时,生成对应的 watcher
并收集依赖。
Data
通过 Observer
转换成了 getter/setter
的形式,来对数据追踪变化。
修改对象的值的时候,会触发对应的 setter
, setter
通知之前依赖收集得到的 Dep
中的每一个 Watcher
,告诉它们值改变了,需要重新渲染视图。
数据双向绑定原理
什么是响应式的原理
- 核心:
Object.defineProperty
- 默认
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中式如何检测数组变化
使用 函数劫持 的方式,重写了数组的方法, vue
将 data
中的数组进行了 原型链的重写 ,指向了自己定义的数组原型方法,这样当调用数组 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()
依次调用 watcher
的 update
, queueWatcher
将 watcher
去重放到队列中, nextTick(flushSchedulerQueue)
异步清空 watcher
队列。
nextTick实现原理
微任务高于宏任务先执行
nextTick
方法主要使用了宏任务和微任务,定义了一个异步方法,多次调用了 nextTick
会将方法存入到队列中,通过这个异步方法清空当前队列。
nextTick
方法是异步方法。
原理: nextTick(cb)
调用 nextTick
传入 cb
, callbacks.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
loader
是 webpack
中一个非常核心的概念
loader
使用过程:
- 通过
npm
安装需要使用的loader
- 在
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.js
, webpack
是 node
写出来的 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
样式:
-
style-loader
将模块的导出作为样式添加到dom
中 -
css-loader
解析css
文件后,使用import
加载,并且返回css
代码 -
less-loader
加载和转译less
文件 -
sass-loader
加载和转译sass/scss
文件 -
postcss-loader
使用PostCSS
加载和转译css/sss
文件 -
stylus-loader
加载和转译Stylus
文件
style-loader
安装:
npm install style-loader --save-dev
用法:
建议将 style-loader
与 css-loader
结合使用
component.js
import style from './file.css'
-
css-loader
只负责将css文件进行加载 -
style-loader
负责将样式添加到dom
中 - 使用多个
loader
时,是从右到左
代码:
// webpack.config.js module: { rules: [ { test: /\.css$/, use: ['style-loader', 'css-loader'] } ] }
css
文件处理: style-loader
安装 style-loader
npm install --save-dev style-loader
style-loader
需要放在 css-loader
的前面, webpack
在读取使用的 loader
的过程中,是按照从右向左的顺序读取的。
webpack.config.js
的配置如下:
const path = require('path') module.exports = { // 入口:可以是字符串/数组/对象,这里我们入口只有一个,所以写一个字符串即可。 entry: './src/main.js', // 出口:通常是一个对象,里面至少包含两个重要属性,path和filename output:{ path: path.resolve(__dirname, 'dist'), // 注意:path通常是一个绝对路径 filename: 'bundle.js' }, module: { rules: { { test: /\.css$/, use: ['style-loader','css-loader'] } } } }
webpack
less文件处理
安装:
npm install --save-dev less-loader less
示例:
将 css-loader
, style-loader
, less-loader
链式调用,可以把所有样式立即应用于 dom
。
// webpack.config.js module.exports = { ... rules: [{ test: /\.less$/, use: [{ loader: 'style-loader' },{ loader: 'css-loader' },{ loader: 'less-loader' }] }] }
图片文件处理
css normal
代码:
body { background: url("../img/test.jpg") }
url-loader
npm install --save-dev url-loader
用法
url-loader
功能类似于 file-loader
,但是在文件大小低于指定的限制时,可以返回一个 DataURL
import img from './image.png'
webpack.config.js
module.exports = { module: { rules: [ { test: /\.(png|jpg|gif)$/, use: [ { loader: 'url-loader', options: { limit: 8192 } } ] } ] } }
img
,文件要打包到的文件夹
name
,获取图片原来的名字,放在该位置
hash:8
,为了防止图片名称冲突,依然使用 hash
,但是我们只保留8位
ext
,使用图片原来的扩展名
es6转es5的babel
如果希望es6转成es5,那么就需要使用 babel
npm install --save-dev babel-loader@7 babel-core babel-preset-es2015
配置 webpack.config.js
文件:
{ test: /\.m?js$/, use: { loader: 'babel-loader', options: { presets: ['es2015'] } } }
使用vue
如何在我们的 webpack
环境中集成 vue.js
代码:
npm install vue --save
-
runtime-only
代码中,不可以有任何的template
-
runtime-compiler
代码中,可以有template
因为有compiler
可以用于编译template
spa
( simple age web application
)-> vue-router
(前端路由)
.vue
文件封装处理
安装 vue-loader
和 vue-template-compiler
npm install vue-loader vue-template-compiler --save-dev
认识webpack的plugin
-
plugin
是什么?
-
plugin
是插件的意思,通常用于对某个现有的架构进行扩展。 -
webpack
中的插件,是对webpack
现有功能的各种扩展。
-
loader
和plugin
的区别
-
loader
主要用于转换某些类型的模块,它是一个转换器。 -
plugin
是插件,它是对webpack
本身的扩展,是一个扩展器。
-
plugin
的使用过程:
- 通过
npm
安装需要使用的plugins
- 在
webpack.config.js
中的plugins
中配置插件
webpack.config.js
的文件:
查看 bundle.js
文件的头部:
Vue Cli详解
什么是 vue cli
, Command-Line Interface
,命令行界面,俗称脚手架, vue cli
是一个官方发布的项目脚手架。使用 vue-cli
可以快速搭建 vue
开发环境以及对应的 webpack
配置。
vue cli
的使用
安装 vue
脚手架
npm install -g @vue/cli
vuecli2初始化过程
代码:
vue init webpack vuecli2test
Project name Project description Author Vue build Install vue-router
目录结构详解
build
`config 是
webpack 相关配置,
node_modules 是依赖的
node 相关的模块,
src 是写代码地方。
.babelrc 是es代码相关转换配置,
.editorconfig 项目文本相关配置,
.gitignore git
仓库忽略的文件夹配置, .postcssrc.js
为 css
相关转化的配置。
.editorconfig
前端模块化:
为什么使用模块化,简单写js代码带来的问题,闭包引起代码不可复用,自己实现了简单的模块化, es
中模块化的使用: export
和 import
。
npm install @vue/cli -g
npm clean cache -force
vue cli2
初始化:
vue init webpack my-project
vue cli3
初始化项目:
vue create my-project
箭头函数的使用和this
箭头函数,是一种定义函数的方式
- 定义函数的方式:
function
const a = function(){ }
- 对象字面量中定义函数
const obj = { b: function() { }, b() { } }
- 箭头函数
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-router
和 koa-router
的区别:
vue-router
是前端路由, koa-router
是后端路由。
vue-router
前端路由原理:
前端路由主要模式: hash
模式和 history
模式。
路由的概念来源于服务端,在服务端中路由描述的是 URL 与处理函数之间的映射关系。
前后端渲染之争
url中的 hash
和 html5
的 history
前端路由的核心是改变 url
,但是页面不进行整体的刷新。单页面,其实 spa
最重要的特点就是在前后端分离的基础上加了一层前端路由。就是前端来维护一套路由规则。
url
的 hash
url
的 hash
是锚点 #
,本质上是改变 window.location
的 href
属性。直接赋值 location.hash
来改变 href
,但是页面不发生刷新。
html5
的 history
模式: pushState
html5
的 history
模式: replaceState
html5
的 history
模式: go
history.go()
history.back()
等价于 history.go(-1)
history.forward()
等价于 history.go(1)
安装 vue-router
npm install vue-router --save
Vue.use(VueRouter) 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
的步骤
- 创建路由组件
- 配置路由映射:组件和路径映射关系
- 使用路由:通过
<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: { } }
-
<router-link>
该标签是一个vue-router
已经内置的组件,它会被渲染成一个<a>
标签 -
<router-view>
该标签会根据当前的路径,动态渲染出不同的组件。 - 网页的其他内容,比如顶部的标题或导航,或者底部的一些版本信息等会和
<router-view>
处于同一个等级。 - 在路由切换时,切换的是
<router-view>
挂载的组件,其他内容不会发生改变。
路由的默认路径
默认情况下,进入网站的首页, <router-view>
渲染首页的内容,但是默认没有显示首页组件,必须让用户点击才可以。
那么如何让路径默认跳转到首页,并且 <router-view>
渲染首页组件呢,只需要配置一个映射就可以:
const routes = [ { path: '/', redirect: '/home' } ]
配置解析:在 routes
中又配置了一个映射, path
配置的是根路径: /
, redirect
是重定向,就是我们将根路径重定向到 /home
的路径下。
// main.js const router = new VueRouter({ // 配置路由和组件之间的应用关系 routes, mode: 'history' })
改变路径的方式:
-
url
的hash
-
html5
的history
- 默认情况下,路径的改变使用的
url
的hash
使用 html5
的 history
模式:
// 创建router实例 const router = new VueRouter({ routes, mode: 'history' })
router-link
,使用了一个属性 :to
,用于指定跳转的路径。 tag
可以指定 <router-link>
之后渲染成什么组件。
replace
属性不会留下 history
记录,指定 replace
的情况下,后退键返回不能返回到上一个页面中。
active-class
属性,当 <router-link>
对应的路由匹配成功时,会自动给当前元素设置一个 router-link-active
的 class
,设置 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>
默认选中:
传递参数的方式
传递参数主要有两种类型, params
和 query
params
的类型:
/router/:id path /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
的类型:
- 配置路由格式:
/router
也是普通配置 - 传递方式,对象中使用
query
的key
作为传递方式 - 传递后形成的路径,
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
是有区别的
-
$router
为VueRouter
实例,想要导航到不同url
,则使用$router.push
方法。 -
$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() })
// 后置钩子hook router.afterEach((to,from) => { console.log(); })
导航守卫:导航表示路由正在发生改变。
vue-router
提供的导航守卫主要用来通过跳转或取消的方式守卫导航。有多种机会植入路由导航过程中,全局的,单个路由独享的,或者组件级的。
全局守卫
可以使用 router.beforeEach
,注册一个全局前置守卫:
const router = new VueRouter({..}) router.beforeEach((to,from,nex)=>{ })
当一个导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫 resolve
完之前一直处于等待中。
-
to:Route
,即将要进入的目标路由对象 -
from:Route
,当前导航正要离开的路由 -
next:Function
,一定要调用该方法来resolve
这个钩子。
vue-router-keep-alive
keep-alive
和 vue-router
router-view
是一个组件,如果直接被包含在 keep-alive
里面,所有路径匹配到的视图组件都会被缓存。
keep-alive
是 Vue
内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染。
属性:
include 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
三种状态:
-
pending
等待状态,比如正在进行网络请求,或定时器没有到时间。 -
fulfill
,满足状态,主动回调resolve
时,并且回调.then()
-
reject
,拒绝状态,回调reject
时,并且回调.catch()
Vuex详解
vuex
是一个专门为 vue.js
应用程序开发的状态管理模式
它采用集中式存储管理应用的所有组件的状态,,并以相应的规则保证状态以一种可预测的方式发生变化。
- 状态管理模式
- 集中式存储管理
View components -> actions(dispatch方式) -> mutations(commit方式) -> state -> View components
Vuex
核心概念5个:
State
, Getters
, Mutation
, Action
, Module
State
单一状态树,单一数据源。
Mutation
状态更新
Vuex
的 store
的更新唯一方式,提交 Mutation
Mutation
的主要包括两部分:
state
mutation
的定义:
mutations: { increment(state) { state.count++ } }
通过 mutation
更新
increment: function() { this.$store.commit('increment') }
参数被称为是 mutation
的载荷 payload
Vuex
的 store
中的 state
是响应式的,当 state
中的数据发生改变时, Vue
组件会自动更新。
store 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({ // 默认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.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 webMall npm 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
安装 nginx
, linux
部署, 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
回车进行解压操作。
小结:
- 计算属性在多次使用时,只会调用一次,因为它是有缓存额
- 修饰符:
stop
,prevent
,.enter
,.once
,.native
等,lazy
,number
,trim
等。 - 模板的分类写法:
script
,template
- 父子组件的通信:父传子,
props
,子传父,$emit
- 项目,
npm install
,npm run serve
-
webStorm
开发vue
在Plugins
安装插件vue.js
- 在
2.6.0
版本中,Vue
为具名插槽和作用域插槽引入了一个新的统一的语法 (即<v-slot>
指令)。它取代了slot
和slot-scope
这两个目前已被废弃、尚未移除,仍在文档中的特性。 -
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>
最后
欢迎加我微信Jeskson( xiaoda0423
),拉你进技术群(掘金前端群,达达前端技术社群⑥),长期交流学习。
个人网站: http://www.dadaqianduan.cn/#/
以上所述就是小编给大家介绍的《【图文并茂,点赞收藏哦!】重学巩固你的Vuejs知识体系》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 故事:走进 JVM 的世界(图文并茂)
- 快速入门 Docker 实战教程 | 图文并茂
- 图文并茂,为你揭开“单点登录“的神秘面纱
- 图文并茂剖析 Netty 编解码以及背后的设计理念
- CSS之轻松搞懂浮动与清除浮动(图文并茂)
- 图文并茂的带你彻底理解悲观锁与乐观锁
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
JAVASCRIPT权威指南(第四版)
David Flanagan / 张铭泽、等 / 机械工业出版社 / 2003-1-1 / 99.00
《JavaScript权威指南》全面介绍了JavaScript语言的核心,以及Web浏览器中实现的遗留和标准的DOM。它运用了一些复杂的例子,说明如何处理验证表单数据、使用cookie、创建可移植的DHTML动画等常见任务。本书还包括详细的参考手册,涵盖了JavaScript的核心API、遗留的客户端API和W3C标准DOM API,记述了这些API中的每一个JavaScript对象、方法、性质、......一起来看看 《JAVASCRIPT权威指南(第四版)》 这本书的介绍吧!