文章目录
一、Vue相关知识
1. 注入
为什么Vue中的配置data,method会提取到vm实例中,这个过程称为注入
-
目的:
-
数据响应式
Vue2.0
通过Object.defineProperty
方法完成数据响应式;Vue3.0
通过Class Proxy
完成数据响应式 -
绑定
this
为什么
this.data/this.method
中的this
指向vue
的实例
-
2 虚拟DOM树
提高渲染效率,vue把模板编译成虚拟DOM树(js对象构成),再生成真实DOM树
当修改元素后,会将前后虚拟DOM树进行对比(虚拟DOM每一次全新生成),最大减少对真实DOM的操作
模板的来源:根标签中的outerHTML
、<template>
标签中、render
函数,优先级逐渐提升
{
render(h): {
return h("h1", val)
}
}
3 挂载
内容放置到哪个元素上面的过程
挂载方式:el
属性、通过vm.$mount('')
延迟挂载
4 完整流程
二、特殊属性
1. watch属性
能够监视data的动态变化
const vm = new Vue({
el: '#app',
data: {
a: 1
},
watch: {
a: {
handler(newValue, oldValue) {
console.log("a被修改了", newValue, oldValue)
}
}
}
})
第二种写法,在创建实例后添加监听属性
vm.$watch('a', {
handler(newValue, oldValue) {
console.log("a被修改了", newValue, oldValue)
}
})
- 深度watch
当存在多层级的属性的时候:
data: {
members: {
a: 1,
b: 1
}
},
此时想要监听members内部的a, b属性,可以写下面的代码:
watch: {
'members.a': {
handler(newValue, oldValue) {
console.log("a被修改了", newValue, oldValue)
}
},
'members.b': {
handler(newValue, oldValue) {
console.log("b被修改了", newValue, oldValue)
}
}
}
但是当members中的属性很多的时候,书写起来就不方便,因此有了深度watch,通过监听members,进而监听内部的属性值
watch: {
members: {
deep: true,
handler() {
console.log("members 被修改了")
}
}
}
- 当计算属性和watch同时能实现的时候,使用计算属性更容易;但是当涉及异步任务的时候应该使用watch
- 所有Vue管理的函数,最好写成普通函数,这样this指向的是vm或组件实例
而使用如 定时器的回调函数,ajax回调函数的时候,应该使用箭头函数,这样this的执行会向外查找到Vue的实例,进而拿到Vue的对象。
2. key的作用
最重要的特殊属性:key
该属性可以干预diff
算法,影响虚拟DOM节点的比较;在同一层级,key
值相同的节点进行对比,key
值不同的重新生成
key是虚拟DOM的标识
当插入数据的时候打乱顺序,使用index作为key在虚拟DOM对比算法的过程中可能因为复用虚拟dom节点的问题导致复用错误的信息;如果比较情况下内容相同,会直接复用旧DOM中的值。
3. 计算属性
{
el: '#app',
data: {
firstName: '',
lastName: ''
},
computed: {
// fullName() {
// return this.firstName + this.lastName
// }
// 完整写法
fullName: {
get() {
return this.firstName + this.lastName
},
set(val) {
console.log('设置器...');
this.firstName = val[0]
this.lastName = val.substr(1)
}
}
}
}
计算属性和方法的区别:
- 计算属性有访问器和设置器,可以对参与计算的属性进行方向操作
- 计算属性具有缓存,如果依赖不变,则不会重新计算
- 一般建议使用已有
data
属性计算的属性内容,设置成计算属性而不是方法
4. ref属性
在vue
中,采用MVVM
理念,不需要直接操作DOM
如果需要操作DOM元素的场景,可以使用 ref 进行操作;每一个
vue
组件实例对象上都有一个$refs
属性可以获取元素对象,默认是空对象
其次,组件实例也有$refs
属性,类似组件传值,如果父组件需要直接操作子组件的属性,可以借助这个属性完成。
- this.$nextTick(cb)
参数cb
为一个回调函数,保证函数执行在DOM
重新渲染完毕继续执行
场景:组件显隐操作,等待组件重新渲染成功后继续执行后续操作
三、 Vue-cli
1. 项目创建
npm install -g @vue/cli // 第一次安装脚手架
vue create xxx // 创建一个项目
npm run serve: 项目执行
npm run build: 将vue编译成.html文件
main.js 入口文件
// 引入Vue
import Vue from 'vue';
// 所有组件的父组件
import App from './App.vue'
new Vue({
// render渲染函数, h表示Vue中的一个createElement函数,用来创建元素
render: h => h(App) // 将App组件放入容器, '#app'
}).$mount('#app')
vue中可以通过ref获取到dom元素,用来给标签或子组件注册引用信息(id的替代者)
props组件传值,如果需要约定类型,可以用对象形式表示
props: {
name: String,
age: Number,
sex: String
}
外部传入的值不能直接修改
Vue.use() 可以使用插件
2. 代理服务器
在前端维护代理服务器,用来解决跨域;代理服务器使用node保证node后台和服务器的交互,而XmlHttpRequest是属于浏览器中的内置对象,为了浏览器的安全因此有了跨域,在引入代理服务器后没有浏览器的参与就不涉及跨域了。
module.exports = {
devServer: {
proxy: 'http://localhost:4000'
}
}
只能配置一个代理
第二种:
module.exports = {
devServer: {
proxy: {
'/api': { // 请求前缀,第一个请求
target: '<url>',
pathRewrite: {'^/api': ''} // 重写请求路径,将前缀去除
ws: true,
changeOrigin: true
},
'/foo': { // 第二个请求
target: '<other_url>'
}
}
}
}
四、组件
1. Html中组件的创建
使用局部组件的方式创建如下代码
<div id="app">
<button @click="onReverse">测试切换</button>
<!-- 通过:is 可以实现动态切换组件的效果,数据变化由组件名称决定 -->
<!-- :props, 用于父组件向子组件数据传递 -->
<!-- :func 为子组件向父组件传递数据时的方法调用 -->
<hello :props="props" @func="onFunc" :is="currentComponent"></hello>
<other :is="!currentComponent"></other>
<div>
<label>我是:</label>
<span>{{ sonHTML }}</span>
</div>
</div>
<!-- 定义模板template begin -->
<template id="hello">
<div>
用户名: {{ props.name }} <br>
<a href="www.baidu.com" @click.prevent="onClick(props)">hello world</a>
</div>
</template>
<template id="other">
<div>其他内容</div>
</template>
<!-- 定义模板template end -->
<script>
// 声明组件 begin
const hello = {
template: '#hello',
props: ['props'],
methods: {
onClick(param) {
this.$emit('func', param.name);
}
}
}
const other = {
template: '#other',
}
// 声明组件 end
// 创建实例
new Vue({
el: '#app',
components: { // 注册组件
hello: hello,
other: other
},
data: { // 声明绑定响应式数据
a: 5,
props: {
name: 'zs',
age: 18
},
sonHTML: '',
currentComponent: 'other'
},
methods: {
onFunc(param) {
this.sonHTML = param;
},
onReverse() { // 组件切换
if (this.currentComponent == 'hello') {
this.currentComponent = 'other'
} else {
this.currentComponent = 'hello'
}
}
}
})
</script>
2. 组件传值
2.1 兄弟组件的传值
vue2
中,兄弟组件之间的数据共享使用EventBus
使用步骤:
- 创建eventBUs.js模块,向外共享一个
Vue
实例对象
import Vue from 'vue'
export default new Vue()
- 数据发送方,调用
bus.$emit()
触发自定义事件
methods: {
handleSendMsg() {
bus.$emit('receive', this.sendMsg)
}
}
- 数据接收方,调用
bus.$on('事件名称','事件处理函数')
注册一个自定义事件
created() {
bus.$on('receive', val => {
console.log('在HelloWorld组件中接收数据:', val);
this.testMsg = val
})
}
3. 动态组件
vue 中提供 <component>
标签,实现动态组件的渲染
<component :is="自定义变量表示组件名称"></component>
动态组件每一次切换都会进行销毁,为了避免组件被销毁对组件进行缓存,可以使用
keep-alive
进行包裹
<keep-alive>
<component :is="自定义变量表示组件名称"></component>
</keep-alive>
通过
include
属性可以控制 keep-alive 缓存哪些组件或者通过
exclude
进行排除,两者不能同时使用
<keep-alive include="Left, Right">
<component :is="自定义变量表示组件名称"></component>
</keep-alive>
4. 插槽
提供vue
开发者灵活封装组件的能力,将不确定、用户指定的部分定义为插槽
每个插槽都应该有一个name
属性,如果不指定,默认名称是default
4.1 具名插槽
可以通过name
属性进行指定
<template v-slot:left>
<p>我是一个插槽</p>
</template>
<!--简写形式-->
<template #left>
<p>我是一个插槽</p>
</template>
<!--组件定义-->
<slot name="left"></slot>
4.2 作用域插槽
插槽中可以定义变量/信息,类似子向父传递信息
<template v-slot:left="scope">
<p>我是一个插槽: msg={{ scope.msg }}</p>
</template>
<!--组件定义
<slot name="left" 自定义变量名="val值"></slot>
-->
<slot name="left" msg="作用域插槽"></slot>
5. 自定义指令
- 私有自定义指令
directives: {
color: {
// bing() 中的el参数表示添加指令的dom元素对象
// binding表示指令参数值
bind(el, binding) {
el.style.color = binding.value
}
}
}
但是bind
只在绑定的时候出现一次,当指令的数据具有响应式,需要使用update
属性
directives: {
color: {
bind(el, binding) {
el.style.color = binding.value
},
update(el, binding) {
el.style.color = binding.value
}
}
}
如果bind
和update
逻辑相似,可以使用简写形式:
directives: {
color(el, binding) {
el.style.color = binding.value
}
}
- 全局自定义指令
在main.js
入口文件中绑定:
Vue.directive('color', function(el, binding) {
el.style.color = binding.value
})
五、混入mixin
<div id="app">
<hello></hello>
<other></other>
</div>
<!-- 定义模板template begin -->
<template id="hello">
<div>
<a href="#" @click.prevent="onClick">hello</a>
</div>
</template>
<template id="other">
<div>
<a href="#" @click.prevent="onClick">other</a>
</div>
</template>
<!-- 定义模板template end -->
$vnode是虚拟DOM树中的节点,全局唯一
<script>
let myMixin = {
methods: {
onClick() {
if (this.$vnode.tag.match('hello')) {
console.log('hello');
this.helloInternal();
} else {
console.log('other');
this.otherInternal();
}
}
}
}
const hello = {
template: '#hello',
methods: {
helloInternal() {
console.log('我是hello内部方法')
}
},
mixins: [myMixin]
}
const other = {
template: '#other',
methods: {
otherInternal() {
console.log('我是other内部方法')
}
},
mixins: [myMixin]
}
new Vue({
el: '#app',
components: {
hello: hello,
other: other
}
})
</script>
当组件和混入对象含有同名选项时,这些选项将以恰当的方式进行“合并”。并以组件中的数据优先显示。