https://juejin.im/post/5d5375a0f265da03db076f59#heading-32
vue基础知识点
1. vue中常见的指令
1.1 数据相关的指令
- v-text:
- 主要用来更新textContent
- 双大括号的方式会将数据解释为纯文本,而非HTML
<span v-text="msg"></span>
<!-- 两者等价 -->
<span>{{msg}}</span>
-
v-html:
- 命令模式可以解释html
<div v-html="rawHtml"></div>
- v-model:
- 用于在表单上创建双向数据绑定
<div id="app">
<input v-model="somebody">
<p>hello {{somebody}}</p>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
somebody:'小明'
}
})
</script>
上面这个例子中直接在浏览器input中输入别的名字,下面的p的内容会直接跟着变。这就是双向数据绑定。
v-model:仅仅是一个语法糖,相当于
:value
+@input
input
事件:当输入框中的数据改变时发生
1.2属性相关的指令:
- v-bind ``>
- 用于绑定html标签中的属性
- v-bind:href="数据"
- 简写: :href = "数据"
- 用于绑定html标签中的属性
<a v-bind:href="hrefname">百度</a>
<a :href="hrefname">百度</a>
<script>
var app = new Vue({
el:"#app",
data: {
hrefname:"http://www.baidu.com",
}
})
</script>
1.3 条件指令:
- v-if v-if可以实现条件渲染,Vue会根据表达式的值的真假条件来渲染元素。
<a v-if="ok">yes</a>
如果属性值ok为true,则显示。否则,不会渲染这个元素。
- v-else v-else是搭配v-if使用的,它必须紧跟在v-if或者v-else-if后面,否则不起作用。
<a v-if="ok">yes</a>
<a v-else>No</a
- v-else-if v-else-if充当v-if的else-if块,可以链式的使用多次。可以更加方便的实现switch语句。
<div v-if="type``='A'">
A
</div>
<div v-if="type``='B'">
B
</div>
<div v-if="type``='C'">
C
</div>
<div v-else>
Not A,B,C
</div>
- v-show
<h1 v-show="ok">hello world</h1>
也是用于根据条件展示元素。和v-if不同的是,如果v-if的值是false,则这个元素被销毁,不在dom中。但是v-show的元素会始终被渲染并保存在dom中,它只是简单的切换css的dispaly
属性。
注意:v-if有更高的
切换开销
v-show有更高的初始渲染开销。 因此,如果要非常频繁的切换,则使用v-show较好;如果在运行时条件不太可能改变,则v-if较好
特别提醒
v-show
需要放到一个真实的dom上,不能放在template
上面
1.4 循环指令:
v-for 遍历数组
<p v-for="(value,index) in links">{{value}}</p>
注意:在使用v-for过程中,要绑定唯一的key
,key尽量不要绑定index,如果有id尽量绑定id
<p v-for="(value,index) in links" :key="index">{{value}}</p>
特别提醒
建议不要在与
v-for
相同的元素上使用v-if
。因为v-for
指令的优先级高于v-if
当它们处于同一节点。v-for
的优先级比v-if
更高,这意味着v-if
将分别重复运行于每个v-for
循环中。
1.5 两个特殊的属性:class style
- class
- :class="数据"
- :class="三元运算符"
- :class="{类名:boolean}"
- :class="{类名1:boolean,类名2:boolean}"
- 类名中有-,需要使用引号包起来
- :class="['类名']"
<style>
.abc{color: red;}
.def{background-color: blue;}
.g-h{color: yellow;}
</style>
<div id="app">
<p class="abc">这是一个p标签</p>
<p :class="className">这是一个p标签</p>
<!-- ? : 三元运算符 -->
<p :class="isPass ? className : ''">这是一个p标签</p>
<p :class="{abc:true}">这是一个p标签</p>
<p :class="{abc:isPass}">这是一个p标签</p>
<p :class="{abc:isPass,def:true}">这是一个p标签</p>
<p :class="{'g-h':true}">这是一个p标签</p>
<p :class="['abc','def']">这是一个p标签</p>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
name:"wangcai",
className:"abc",
isPass:true,
}
})
</script>
- style的用法
<div id="app">
<p :style="myStyle">这是一个p标签</p>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
myStyle:{color:'red',backgroundColor:'blue'}
}
})
</script>
2. vue 中挂载的属性及方法
2.1 el
el指定app管理的边界
面试题
如何通过
$el
获取到更新后的dom元素? 答:在下一下事件环中得到更新后dom元素
2.2 data
数据模型
2.3 methods
2.4 computed
计算属性,依赖于data中的数据
计算属性和方法的区别:
- 1,它们两个本质都是函数
- 2,用的时候,方法需要调用,计算属性是当成数据来使用
- 3,方法没有缓存,计算属性有缓存
2.5 filters
过滤器
过滤器的简单使用:
2.6 components
组件:三部曲
- 定义组件
- 注册组件
- 使用组件
组件的简单使用:
2.7 钩子函数
生命周期方法
2.8 $watch
$watch
可以监控Data中的数据,通过$watch
得到值,肯定是更新后的值 使用
vm.$watch( "name", function(newValue,oldValue){ } )
2.9 $nextTick()
vue中的$nextTick主要涉及到vue中DOM的异步更新 nextTick
应用场景及原因
- 在Vue生命周期的
created()
钩子函数进行的DOM操作一定要放在Vue.nextTick()
的回调函数中
原因:在
created()
钩子函数执行的时候DOM 其实并未进行任何渲染,而此时进行DOM操作无异于徒劳,所以此处一定要将DOM操作的js代码放进Vue.nextTick()
的回调函数中。与之对应的就是mounted()
钩子函数,因为该钩子函数执行时所有的DOM挂载和渲染都已完成,此时在该钩子函数中进行任何DOM操作都不会有问题 。
- 在数据变化后要执行的某个操作,而这个操作需要使用随数据改变而改变的DOM结构的时候,这个操作都应该放进
Vue.nextTick()
的回调函数中。
Vue的官方文档中详细解释
Vue 异步执行 DOM 更新。只要观察到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据改变。如果同一个 watcher 被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作上非常重要。然后,在下一个的事件循环“tick”中,Vue 刷新队列并执行实际 (已去重的) 工作。Vue 在内部尝试对异步队列使用原生的
Promise.then
和MessageChannel
,如果执行环境不支持,会采用setTimeout(fn, 0)
代替。
例如,当你设置
vm.someData = 'new value'
,该组件不会立即重新渲染。当刷新队列时,组件会在事件循环队列清空时的下一个“tick”更新。多数情况我们不需要关心这个过程,但是如果你想在 DOM 状态更新后做点什么,这就可能会有些棘手。虽然 Vue.js 通常鼓励开发人员沿着“数据驱动”的方式思考,避免直接接触 DOM,但是有时我们确实要这么做。为了在数据变化之后等待 Vue 完成更新 DOM ,可以在数据变化之后立即使用Vue.nextTick(callback)
。这样回调函数在 DOM 更新完成后就会调用。
2.10 $set
$set
可以给data中动态地添加一个数据,前提
是只能给对象中添加数据
示例
3. 事件模型
事件三要素: 事件源,事件类型,监听器
语法
:v-on: 处理事件 或 @处理事件
3.1 事件类型
- 鼠标事件:
- click:单击事件
- dblclick:双击事件
- mouseover:鼠标进入事件
- mouseout:鼠标移开事件
- mouseenter:鼠标进入事件
- mouseleave:鼠标离开
- mousedown:鼠标按下
- mousemove:鼠标移动
- mouseup:鼠标抬起
- 键盘事件:
- keyup:键盘抬起
- keypress:按键并产生第一个字符时
- keydown:键盘按下
- UI事件:
- scroll:滚动条事件
- resize:浏览器窗口改变
- load:文档加载完毕
- unload:文档未加载
- 焦点事件:
- focus:焦点事件
- blur:失去焦点
- 表单事件:
- change:数据发生改变时
- submit:提交
注意:mouseover / mouseout 与 mouseenter / mouseleave的区别
- 当绑定事件的元素里面没有子元素的时候,两组触发效果是一致的。
- 当绑定事件的元素里面有子元素的时候,鼠标经过绑定mouseover的当前元素以及它里面的子元素的时候,都会触发, 而经过绑定mouseenter的元素时,只会在鼠标刚进入的时候触发,当进入其子元素的时候,是不会再触发的了。
总结
:
不论鼠标指针穿过被选元素或其子元素,都会触发 mouseover 事件。对应mouseout
只有在鼠标指针穿过被选元素时,才会触发 mouseenter 事件。对应mouseleave
3.2 事件对象
当事件发生时,事件对象中保存了很多信息,如点击的坐标。 $event
固定的名字
示例:
3.3 事件修饰符
- stop:阻止冒泡
- prevent:阻止默认事件
- capture:冒泡改为捕获
- self:只处理发生在自己身上的事件,不理会冒泡或捕获(把self放到谁的身上,谁就不会触发)
- once:只执行一次
示例:
3.4 键盘事件修饰符
- keyCode 键盘修饰符
示例:
- 自定义按键名称
// 自定义按键名称
Vue.config.keyCodes.ent = 13;
// 页面引用
<input type="text" @keyup.ent="enter_click"/>
4. 组件
4.1 全局组件
全局组件 : 定义全局组件,在每个组件中都可以进行引用
语法:
Vue.component("组件名",{template:"组件内容"})
示例:
4.2 局部组件:
组件:三部曲
- 定义组件
- 注册组件
- 使用组件
示例:
5. 组件之间的数据传递
5.1 父传子
父传子 : 父中有数据,传递给子,
步骤:
1,确定父中有数据 2,在父组件的模板中通过属性绑定把数据绑到子组件上 3,在子组件中定义props属性,用来接收父传递过来的数据 4,在子组件的模板就可以使用接收过来的数据了
示例:
属性校验:
总结:
父绑定数据 子接收数据
5.2 子传父
过程:
- 在父组件模板中,给子组件添加一个事件监听
- 在子组件中,某个时间通过
this.$emit
发出这个事件,发出事件的同时就可以携带数据- 当父中的方法触发,数据作用这个方法的第一个参数
示例:
6. vue脚手架
6.1 脚手架的安装
- 安装node
2. 安装nrm
npm i nrm -g
-
切换安装源
查看安装源
nrm ls
切换安装源 nrm use taobao
安装vue的脚手架
npm i -g @vue/cli
- 创建vue项目
vue create myvue
- 进入项目
cd myvue
- 启动项目
npm run serve
- 项目目录
6.2 组件的使用
- 组件的定义
2. 组件的使用(main.js主接口)
7. router(路由)的使用
7.1 基本路由
html代码
<div id="app">
<h1>Hello App!</h1>
<p>
<!-- 使用 router-link 组件来导航. -->
<!-- 通过传入 `to` 属性指定链接. -->
<!-- <router-link> 默认会被渲染成一个 `<a>` 标签 -->
<router-link to="/foo">Go to Foo</router-link>
<router-link to="/bar">Go to Bar</router-link>
</p>
<!-- 路由出口 -->
<!-- 路由匹配到的组件将渲染在这里 -->
<router-view></router-view>
</div>
script代码
// 1\. 定义 (路由) 组件。
// 可以从其他文件 import 进来
const Foo = { template: '<div>foo</div>' }
const Bar = { template: '<div>bar</div>' }
// 2\. 定义路由
// 每个路由应该映射一个组件。 其中"component" 可以是
// 通过 Vue.extend() 创建的组件构造器,
// 或者,只是一个组件配置对象。
const routes = [
{ path: '/foo', component: Foo },
{ path: '/bar', component: Bar }
]
// 3\. 创建 router 实例,然后传 `routes` 配置
// 你还可以传别的配置参数, 不过先这么简单着吧。
const router = new VueRouter({
routes // (缩写) 相当于 routes: routes
})
// 4\. 创建和挂载根实例。
// 记得要通过 router 配置参数注入路由,
// 从而让整个应用都有路由功能
const app = new Vue({
router
}).$mount('#app')
// 现在,应用已经启动了!
通过路由
我们可以在任何组件内通过
this.$router
访问路由器,也可以通过his.$route
访问当前路由:
7.2 嵌套路由
- 嵌套路由的组件
- router的注册
7.3 动态路由
- list列表 需要动态跳转的列表
- router.js中对路由进行相关的配置
- 调转页面对数据进行获取
7.4 编程路由
格式
router.push(location, onComplete?, onAbort?)
注意:
在 Vue 实例内部,你可以通过
$router
访问路由实例。因此你可以调用this.$router.push
。
示例:
7.5 重定向
示例:
8. axios的使用
- 安装axios
npm install axios
- 引入axios
import axios from 'axios';
- axios的简单使用
9. vue中的生命周期方法
当vue的实例创建时,会伴随一系列的方法运行,称作生命周期函数
9.1 beforeCreate()
- 这是第一个生命周期函数,表示实例完全被创建之前会被执行
- 该方法执行时,data和methods中的数据还没有初始化
9.2 created()
- 该方法执行时,data与mathods中的数据已经初始化好了
- 要操作data,methods中的数据,最早只能在该处进行操作
9.3 beforeMount()
- 表示模板已经在内存中编辑完成,但尚未将模板渲染到页面中
- 该方法执行时,页面中的元素没有真正替换过来,只是一些模板字符串
9.4 mounted()
- 表示内存中的模板,已经挂载到页面中,用户可以看到渲染后的页面
- 如果通过某些插件操作页面上的DOM节点,最早在该方法中执行
9.5 beforeUpdate()
- 表示:当前页面没有更新,但是数据已经更新
9.6 updated()
- 表示:页面和数据均已经更新
9.7 beforeDestory()
- 实例从运行阶段进入销毁阶段,此时实例中的东西仍处于可用状态
9.destoryed()
- 所有组件完全被销毁
10. vue中组件之间的传值方式
10.1 props 与 $emit
父组件向子组件传递数据是通过prop传递的,子组件传递数据给父组件是通过$emit触发事件来做到的。
props 与 $emit的简单使用
注意
: 使用$emit 触发父类绑定的方法时,触发的名称必须全部是小写,若父类绑定使用到小驼峰,则在触发时,全部变成小写,驼峰之间用-隔开
10.2 attrs和listeners
可以实现父组件与孙子组件之间的数据的传递 this.attrs如果子接受的数据没有用到,把数据保存在attrs中
$attrs的简单使用
特别提醒
父组件中所绑定的传给父组件的属性,都存放在$attrs中,若在子组件中不接收,直接进行绑定,可以使用:
v-bind="this.$attars"
$listenners的简单使用
触发父传递的click事件: @click="listeners.click()"触发父传递多个事件:v-on="listeners"
10.3 parent与children
-
$parent 获取父组件的实例,可以以此来调用父组件中的方法。
- 示例:
this.$parent.cut();
-
$children 获取所有的儿子, 可以以此来获取子组件中的数据
- 示例:
this.$children[0].msg = "张三"
特别提醒
parent: 获取的是父亲的实例children:获取的是所有的子组件的实例
children:获取的是所有子组件的实例
10.4 provice 与 inject的使用
10.5 refs的使用
ref 与refs是成对使用 ref 用于定义 $refs用户获取
10.6 自定义$dispatch
$dispatch
是挂载到Vue实例
上的一个方法, 自动向上
父类中查找触发的方法,如果该方法存在就触发
示例
10.7 事件总线eventBus
原理
事件总线是,在vue的
原型
上,挂载一个$eventBus
事件(名字自己起),通过$on
进行绑定事件, 先在事件中心中注册一些事件 在需要的位置就可以通过$emit
去发布事件(触发事件)
示例
10.8 数据传递的语法糖
在数据进行传递的时候,数据的传递与触发的方法结合,可以有比较简便的语法糖
,更加方便程序的开发,但是与此同时也有一定的难度,不是很容易懂。下面就简单的介绍几种语法糖。
10.8.1 .sync
语法糖
.sync
是@update:data
的语法糖,data
是vue中data中的数据
在给子类传递数据的时候,通过.sync
对属性进行限定,在子类中可以直接触发update:数据名称
来修改父类中对应的数据,该方法可以省去父类中修改属性的方法。
没有使用.sync
之前
- 传递数据,绑定属性
- 修改数据值,绑定修改的方法
- 触发绑定的修改方法,调用修改方法
示例
使用.sync
之后
- 传递数据,绑定属性.sync
- 触发update:数据名,直接修改数据
没有简化: 简化后:
示例
特别注意
update是规定的触发的方法,update冒号后面跟的必须是父类中要修改的数据的数据名
10.8.2 v-model
语法糖
v-model
是:value
和@input
的语法糖,绑定的属性必须是value
在给子类传递数据的时候,直接使用v-model
进行绑定,在子类中,可以触发input
事件,来达到修改数据的目的
示例
特别注意
v-model
相当于:value
+@input
相当于上述方法的简写
11 vue中数据监控的原理(数据劫持)
数据监控: : 监控数据的获取与改变,可以简称为数据劫持。
vue中data数据的监控原理:
代码如下:
// 数据的监控,监控数据的获取与改变
// vue中data数据的监控原理
obj = {
name: "wangcai",
age:18
};
function obServe(obj){
if(typeof obj ``= "object"){
for(key in obj){
defineReactive(obj,key,obj[key]);
}
}
}
function defineReactive(obj,key,value){
obServe(value);
Object.defineProperty(obj,key,{
get(){
console.log("get...");
return value;
},
set(val){
console.log('set... :');
obServe(val);
value = val;
}
})
}
// defineProperty是数组是无效的
//解决方式如下:
// vue中封装了数据的所有的方法,此处只做列举
let arr = ["push","slice","shift","unshift"]
arr.forEach(method=>{
let old = Array.prototype[method];
Array.prototype[method] = function(value){
console.log("set...")
old.call(this,value)
}
})
//-----------------------------------------------------------
// 测试数组
// observer(obj)
// obj.age = [1,2,3] // set..
// obj.age.push(4) // 设置值 defineProperty对数据是无效的
//-----------------------------------------------------------------
// 测试简单的对象
// obServe(obj)
// console.log(obj.name) //get...
// obj.name = "hello" // set...
// 测试嵌套的对象
// observer(obj)
// obj.name // get...
// obj.name = "hello" // set...
// obj.age = { k:"kkkk" } // set...
// obj.age.k = "mmmm" // set...
12. 路由跳转的传值方式
在vue的路由跳转中有两种方式,分别是问号模式,和冒号模式,两者不能进行混合使用,
12.1 问号传值方式
格式
传值
:<router-link to="/user/detail?id=1">用户1</router-link>
获取值
:{{this.$route.query.id}}
注意
:使用问号进行传值的时候不需在router中特别设置路由
12.2 冒号传值方式
格式
传值
:<router-link to="/user/detail/2">用户2</router-link>
配置router路由文件
path:"detail/:id",
获取值
:{{this.$route.params.id}}
13. 路由的生命周期方法
13.1 全局路由守卫
在全局中有三个路由守卫,分别是beforeEach
,beforeResolve
,afterEach
-
beforeEach
- 路由正在开始发生改变的时候开始调用。
- 格式:
router.beforeEach((to, from, next) => {}
-
beforeResolve
- 所有组件中的路由守卫解析完毕之后,被调用
- 格式:
router.beforeResolve((to, from, next) => {}
-
afterEach
- 当前的路由进入完毕之后,被调用
- 全局后置钩子,不接受next函数
- 格式:
router.afterEach((to, from) => {}
13.2 路由独享的守卫
- beforeEnter
- 当进入到路由的配置中,被调用
格式:
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
]
})
13.3 组件内的守卫
在组件的内部存在整合三个路由组件,分别是beforeRouteEnter
、beforeRouteUpdate
、beforeRouteLeave
-
beforeRouteEnter
- 在进入组件之前,被调用,
- 此时的组件实例没有被创建,
不能
获取组件实例this
- 示例:
-
beforeRouteUpdate
- 当前路由发生改变的时候,
- 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
- 可以访问组件实例
this
- 格式:
beforeRouteUpdate ((to, from, next) => {}
-
beforeRouteLeave
- 当离开该组件的时候被调用,
- 可以访问组件实例
this
- 格式:
beforeRouteLeave((to, from, next) => {}
13.4 总结
- 导航被触发。
beforeEach
- 在失活的组件里调用离开守卫
beforeRouterLeave
。 - 调用全局的
beforeEach
守卫。 - 在重用的组件里调用
beforeRouteUpdate
守卫 (2.2+)。 - 在路由配置里调用
beforeEnter
。 - 解析异步路由组件。
- 在被激活的组件里调用
beforeRouteEnter
。 - 调用全局的
beforeResolve
守卫 (2.5+)。 - 导航被确认。
- 调用全局的
afterEach
钩子。 - 触发 DOM 更新。
- 用创建好的实例调用
beforeRouteEnter
守卫中传给 next 的回调函数。
14 vue.config的简单配置
14.1 vue的简单配置+前端代理解决跨域
源码
// 基于node的 node不支持import语法
// 默认环境变量 NODE_ENV 生产环境 production development
let path = require('path');
module.exports = {
// 配置开发环境(npm run serve)或生产环境(npm run build)url
publicPath:process.env.NODE_ENV ``= 'production'? 'http://www.abcd.cn':'/',
//打包后的资源放入的文件夹,默认未放入文件夹
assetsDir:'asserts',
//打包后生成的文件夹,默认是dist
outputDir:'./dist',
// 是否使用自己的内部模板 一般不使用
runtimeCompiler:false,
// 打包后删除生成的.map文件 不再使用sourcemap
productionSourceMap:false,
chainWebpack:config=>{
// 可以获取到webpack的配置 在增加一些自己的逻辑
// 配置目录别名 别名叫+
config.resolve.alias.set('_c',path.resolve(__dirname,'src/components'));
config.resolve.alias.set('_v',path.resolve(__dirname,'src/views'));
},
//webpack配置,到时候会合并到默认的webpack中。
configureWebpack:{ // webpack-merge
plugins:[],
module:{}
},
// 开发 服务时使用 上线时不需要 解决跨域的问题
devServer:{
proxy:{
'/api/getUser':{
target:'http://localhost:3000',
pathRewrite:{
'/api':''
}
}
}
},
//第三方插件的配置
pluginOptions: {
'style-resources-loader': {
preProcessor: 'less',
patterns: [
path.resolve(__dirname,'src/assets/common.less')
]
}
}
}
14.2 后端代理解决跨域问题
源码
let express = require('express');
let app = express();
// 在后端配置,让所有的人都可以访问我的api接口
app.use('*', function (req, res, next) {
// 允许哪些客户端来访问我
res.setHeader("Access-Control-Allow-Origin","*");
// 允许可以添加哪些头,然后来访问我
res.setHeader("Access-Control-Allow-Headers","*");
// res.header('Access-Control-Allow-Headers', 'Content-Type,Authorization');
// 允许哪些方法来访问我
res.setHeader("Access-Control-Allow-Methods","OPTIONS,PUT,DELETE");
// res.header('Access-Control-Allow-Methods', '*');
// 可以每隔半小时,来发送一个options请求--试探请求
res.setHeader("Access-Control-Max-Age","1800");
// 请求的类型编码
res.header('Content-Type', 'application/json;charset=utf-8');
// 允许客户端携带凭证,处理cookie信息,如果有,并且不对每次请求都新开一个session
res.setHeader("Access-Control-Allow-Credentials",true);
next();
});
15. 路由元信息
路由的元信息一般可用于权限的校验
定义路由的时候可以配置 meta
字段
配置meta字段,该字段配置为对象信息
一个路由匹配到的所有路由记录会暴露为 $route
对象 (还有在导航守卫中的路由对象) 的 $route.matched
数组。因此,我们需要遍历 $route.matched
来检查路由记录中的 meta
字段。
示例:
</article>