计算属性 vs 方法
- 计算属性的调用是有缓存的,方法没有
计算属性 vs 监听属性
- 计算属性:当需要展示的时候,如果包含的数据变化才立马计算出结果,如果数据没变化就直接返回缓存
- 监听属性:当需要展示的时候,数据早就变化的话,系统会异步进行计算,不变化就不调用
计算属性的setter
- 可以对计算属性进行set
class属性
<div v-bind:class="{ active: isActive, 'text-danger': hasError }"></div>
data: {
isActive: true,
hasError: true
}
// 或者
<div v-bind:class="[activeClass, errorClass]"></div>
$scopedSlotsdata: {
activeClass: 'active',
errorClass: 'text-danger'
}
// 或者
<div v-bind:class="[{ active: isActive }, errorClass]"></div>
data: {
isActive: true,
errorClass: 'text-danger'
}
// html结果都为
<div class="active text-danger"></div>
style属性
<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
data: {
activeColor: 'red',
fontSize: 30
}
// 或者
<div v-bind:style="styleObject"></div>
data: {
styleObject: {
color: 'red',
fontSize: '30px'
}
}
// html结果都为
<div style="{ color: 'red', fontSize: '30px' }"></div>
// style写法
>>> .btn{ background:green; } // 打包后 => [data-v-xxxxx] .btn{...}
::v-deep .btn{ background:green; } // 打包后 => [data-v-xxxxx] .btn{...}
:v-deep(.btn){ background:green; } // 打包后 => [data-v-xxxxx] .btn{...}
v-if vs v-show
- v-if 是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。
- v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
- 相比之下,v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。
- 一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。
不推荐同时使用 v-if 和 v-for。
- 当 v-if 与 v-for 一起使用时,v-for 具有比 v-if 更高的优先级。请查阅列表渲染指南以获取详细信息。
v-for指令
key属性是遍历出来的节点的唯一属性,为了帮助vue跟踪节点身份
遍历数组
data: {
items: [
{ message: 'Foo' },
{ message: 'Bar' }
]
}
// 以下都一样
<li v-for="(item, index) in items">
<li v-for="item in items" :key="item.message">
<li v-for="item of items">
遍历对象
data: {
object: { title: 'How to do lists in Vue', author: 'Jane Doe', publishedAt: '2016-04-10' }
}
<li v-for="value in object">
{{ value }}
</li>
//How to do lists in Vue
//Jane Doe
//2016-04-10
<div v-for="(value, name) in object">
{{ name }}: {{ value }}
</div>
//title: How to do lists in Vue
//author: Jane Doe
//publishedAt: 2016-04-10
<div v-for="(value, name, index) in object">
{{ index }}. {{ name }}: {{ value }}
</div>
//0. title: How to do lists in Vue
//1. author: Jane Doe
//2. publishedAt: 2016-04-10
- filter()、concat() 和 slice()只会返回一个新数组
对于对象
Vue 无法检测 property 的添加或移除。所以 property 必须在 data 对象上存在才能让 Vue 将它转换为响应式的。
var vm = new Vue({
data:{
a:1,
info:{
name:'小明'
}
}
})
// `vm.a` 是响应式的
vm.b = 2
// `vm.b` 是非响应式的
对于已经创建的实例,Vue 不允许动态添加根级别的响应式 property。但是,可以使用 Vue.set(object, propertyName, value) 方法向嵌套对象添加响应式 property。
Vue不允许动态添加根级响应式属性,向嵌套对象添加响应式属性。
例如:
Vue.set(vm.info, ‘sex’, ‘男’)
在这里插入代码片
还可以使用 vm.$set 实例方法,这也是全局 Vue.set 方法的别名:
this.$set(vm.info,'sex', '男')
有时你可能需要为已有对象赋值多个新 property,比如使用 Object.assign() 或 _.extend()。但是,这样添加到对象上的新 property 不会触发更新。在这种情况下,你应该用原对象与要混合进去的对象的 property 一起创建一个新的对象。
// 代替 `Object.assign(this.info, { 'sex': '男', 'name': '老王' })`
this.someObject = Object.assign({}, vm.info, { 'sex': '男', 'name': '老王' })
对于数组
Vue 不能检测以下数组的变动:
当你利用索引直接设置一个数组项时,例如:vm.items[indexOfItem] = newValue 当你修改数组的长度时,例如:vm.items.length = newLength 举个例子:
var vm = new Vue({
data: {
items: ['a', 'b', 'c']
}
})
vm.items[1] = 'x' // 不是响应性的
vm.items.length = 2 // 不是响应性的
为了解决第一类问题,以下两种方式都可以实现和 vm.items[indexOfItem] = newValue 相同的效果,同时也将在响应式系统内触发状态更新:
// Vue.set
Vue.set(vm.items, indexOfItem, newValue)
// Array.prototype.splice
vm.items.splice(indexOfItem, 1, newValue)
也可以使用 vm.$set 实例方法,该方法是全局方法 Vue.set 的一个别名:
vm.$set(vm.items, indexOfItem, newValue)
为了解决第二类问题,你可以使用 splice:
vm.items.splice(newLength)
对于插槽
匿名插槽
Home.vue
<child>插槽内容</child>
Child.vue
<div>这是插槽</div>
<slot></slot>
具名插槽
Home.vue
<child>
<template #header>插槽内容1</template>
<template v-slot:default="{d}"><!-- 回传 -->
<button >传递给子组件的插槽 => {{d}}</button>
</template>
<template #footer>插槽内容2</template>
</child>
Child.vue
<div>这是插槽</div>
<slot name='header'></slot>
<slot :d="d" name="default"></slot>
<slot name='footer'></slot>
作用域插槽
Home.vue
<child>
<template #header>插槽内容1</template>
<template #footer='{arr}'>// 输出arr
<div v-for='(item,index) in arr' :key='index'>
{{ item }}
</div>
插槽内容2
</template>
</child>
Child.vue
<div>这是插槽</div>
<slot name='header'></slot>
<slot name='footer' :arr='arr'></slot>
data(){
return {
arr:[1,2,3,4]
}
}
PS:
// element-ui下的slot-scope="scope"就是回传
<template slot-scope="scope">
{{scope.row}}
</template>
对于路由
路由的模式:history、hash
区别:
- 关于找不到当前页面发送请求的问题
history会给后端发送一次请求而hash不会,可配置404页面解决此问题 - 关于项目打包后前端直接打开自测问题
hash是可以看到内容的
history默认情况是看不到内容的 - 关于表象不同
hash:#
history:/
路由跳转自身莫名故障,解决方式:
import VueRouter from 'vue-router'
const routerPush = VueRouter.prototype.push
VueRouter.prototype.push = function (location) {
return routerPush.call(this, location).catch(error => error)
}
守卫:
- 全局守卫
router.beforeEach 路由进入之前 白名单、用户信息加载、菜单加载、路由逻辑重定向 常用
router.afterEach 路由进入之后 - 路由独享守卫
每一个路径.beforeEnter 路由进入之前 - 组件内守卫
component.beforeRouteEnter
路由进入之前 需要next() next(false)
在渲染该组件的对应路由被确认前调用
不!能!获取组件实例this
因为当守卫执行前,组件实例还没被创建
参数 就是未来的this对象
component.beforeRouteUpdate
路由更新之前 需要next() next(false)
在当前路由改变,但是该组件被复用时调用
?id=1 变化 ?id=2
举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
可以访问组件实例this
component.beforeRouteLeave
路由离开之前 需要next() next(false)
导航离开该组件的对应路由时调用
可以访问组件实例this
路由跳转
// 添加历史记录
this.$router.push(this.Index1)
// 不添加历史记录
this.$router.replace(this.Index1)
next({ ...this.Index1, replace: true })
数据绑定
<h2>{{text}}</h2>
<h2 v-text="text"></h2>
<div v-html="myHtml"></div>
data(){
return {
text:'hello Green',
myHtml:`<button class="btn">按钮</button>`
}
}
单项数据绑定
<input type="text" v-bind:value="val" />
<input type="text" :value="val" />
{{ val }}
<input type="text" v-bind="obj" />// value="xxxx" abc="123" ddd="111" hhh="111"
data(){
return {
val:'Green',
obj:{
value:'xxxx',
abc:'123',
ddd:111,
hhh:111
}
}
}
双向数据绑定
<input type="text" v-model="val">
data(){
return {
val:'123'
}
}
v-model一定有input事件,双向绑定,内部可通过input事件修改model绑定的值
this.$emit('input',false);
子组件给设置v-model的时候,value就是对应的值,可直接修改
v-if和v-show
- show是是否显示的问题, if是插入或移除的问题
- 在项目中多使用if 可以减少元素
- 在频繁显示与隐藏的情况下, 可以使用v-show
v-on
<button v-on:click="change">change点击测试</button>
<button v-on:click="change('测试')">change点击(传参)</button>
<button v-on:click="change('测试',$event)">change点击(传事件对象)</button>
<!-- 2.5之后 动态绑定事件click mouseover -->
<button @[action]="change('动态参数的事件',$event)">动态参数的事件</button>
data(){
return {
isShow:false,
action:'mouseover'
}
}
v-for
<button v-for="num in arr" :key="num">{{num}}</button>
<button v-for="(val,key,index) in obj" :key="index">
val:{{val}}|| key: {{key}} || index:{{index}}
</button>
<button v-for="([key,val],index) in map" :key="index">
key:{{key}} || val:{{val}} || index:{{index}}
</button>
<button v-for="(val,index) in set" :key="index">
val:{{val}} || index:{{index}}
</button>
data() {
return {
arr: [1, 2, 3, 4],
obj: { a: 1, b: 2 },
map:new Map([['c',3],['d',4]]),
set:new Set(['e','f','g'])
};
}
指令
v-demo:foo.a.b="message"
message:"Green"
- name:指令名 => “demo”
- value:指令的绑定值 => “Green”
- expression:字符串形式的指令表达式 => “message”
- arg:传给指令的参数,可选 => “foo”
- modifiers:一个包含修饰符的对象 => {a: true, b: true}
// <button v-test>全局指令指令测试</button>
Vue.directive("test", {
bind(element, binding, vnode) {
// 元素初始化 1
console.log("bind", arguments);
},
inserted(element, binding, vnode) {
// 元素插入 2
console.log("inserted:", arguments);
},
update(element, binding, vnode, oldVnode) {
// 元素更新 ,不保证子元素更新完成 3
console.log("update:", arguments);
},
componentUpdated(element, binding, vnode. oldVnode) {
// 元素更新 ,保证子元素更新完成 4
console.log("componentUpdated:", arguments);
},
unbind(element, binding, vnode) {
// 解绑,元素移除 5
console.log("unbind:", arguments);
}
});
// <button v-test1>非全局指令测试</button>
export default {
data() {
return {
};
},
directives: {
test1: {
bind(element, binding, vnode) {
// 元素初始化
let that = vnode.context; // 获取this
element.setAttribute(binding.arg, binding.value) // 给dom节点设置属性为:指令参数=指令绑定值
console.log("bind", arguments);
},
inserted(element, binding, vnode) {
// 元素插入
let parent = el.parentNode // 获取使用指令的元素的父节点dom
console.log("inserted:", arguments);
},
update(element, binding, vnode, oldVnode) {
// 元素更新 ,不保证子元素更新完成
console.log("update:", arguments, oldVnode);
},
componentUpdated(element, binding, vnode) {
// 元素更新 ,保证子元素更新完成
console.log("componentUpdated:", arguments);
},
unbind(element, binding, vnode) {
// 解绑,元素移除
console.log("unbind:", arguments);
}
}
}
};
监听属性
watch:{
num:{
handle(newV, oldV){},
immediate: true // 立即执行
},
obj:{
handle(newV, oldV){},
deep: true // 监视深层对象
},
'obj2.num':{ // 简单路径支持
handle(newV, oldV){}
}
}
data需要ruturn里面的原因
data(){
this.str1 = '222'
// 单纯不能修改数据,因为没get/set
return {
str2:'1111'
}
}
:xxx.sync=“eventFun”
可以利用组件已注册的事件中进行回调
this.$emit('update:text','Red');
render
jsx使用h函数
render(h){
return h('div',
{
style:{background:'yellow'},
props:{ mytest: 'abc},
on: {click:()=>{alert(1))}}
}, [
h('h3', {}, '1'),
h('h3', {}, '2'),
h('h3', {}, '3'),
])
}
结合模板和jsx
render(){
return (
<div style={{background:'yellow'}} test={} onClick={}>
<h2>1</h2>
</div>
)
}
通过名称获取组件
<组件名称 />
const comp = this.$options.components['组件名称']
this.$nextTick(()=>{
new Vue({
render: comp.render
}).$mount('#组件名称')
})
动态组件
<component :is="dynamicName" />
data() {
return {
compsName:['AComp','BComp','CComp'],
dynamicName: 'BComp'
}
},
内置点击外部触发方法
<span v-clickoutside="close">test</span>
import Clickoutside from 'element-ui/src/utils/clickoutside'
methods:{
close(){
this.$message('外部被点击')
}
}
获取scopeId
let scopeid = this.$options._scopeId // 获取了scopeId
this.$refs.ele.setAttribute(scopeid) // 给元素强行设置scopeid
冒泡
// 阻止向下冒泡
<div class="content" @click.self="cancelFunc"></div>
// 阻止向上冒泡
<div class="item" @click.stop="function">
在jsx中写入html格式的字符串
let htmlStr = '<div>你好啊</div>';
<div class="custom-html-box" domPropsInnerHTML={ htmlStr } ></div>
在jsx中应用插槽
scopedSlots = {
default:(prop)=>{
return <div>插槽内容</div>
}
}
<el-table-column scopedSlots={ scopedSlots }></el-table-column>
// 相当于
<el-table-column>
<template slot="default">
<div>插槽内容</div>
</template>
</el-table-column>
在jsx中应用自定义好的指令
const directives = [
{
name: 'drag',
},
];
<el-table {...{ directives }}>
过滤器
Vue.filter('birthday', (val, ...args) => {
return fn(val)
})
按对象固定格式生成filter
const dataMap = {
sex: {
man: '男',
woman: '女'
},
education: {
college: '大学',
highschool: '高中'
}
}
for (let name in dataMap) {
// 模板:'man' | sex('白骨精','白龙马')
// 代码:let fn = Vue.filter.bind(Vue);
// fn('sex')('man')
Vue.filter(name, (val, ...args) => {
// args=>['白骨精','白龙马']
return dataMap[name][val];
})
}
axios
const reqInstance = Axios.create({
baseURL: '/api',
timeout: 5000,
transformResponse: [transPager, transData]// 对返回的数据进行格式化
});
// 拦截器 要么 return 参数 要么 return Promise 后续.resolve() 调用的时候就能继续这次拦截的行为
reqInstance.interceptors.request.use((config) => {
if (config.fullLoading) {
service = Loading.service(); // 显示loading
}
let token = window.sessionStorage.getItem('token');
if (token) {
config.headers['token'] = token;
}
return config;
});
reqInstance.interceptors.response.use((response) => {
if (response.config.fullLoading) {
service.close(); // 关闭loding
}
// 存储token
let token = response?.data?.data?.token;
if (token) {
window.sessionStorage.setItem('token', token);
}
// 业务异常
// code === 200
// else if code === 401
if (response?.data?.code === 20000) {
} else if (response?.data?.code === 401 || response?.data?.code === 603) {
// 行为 replace:
router.replace({ // 401页面
name: 'privilege'
});
} else {
if (response?.data instanceof Blob) {
} else {
Notification.error(response?.data?.data)
}
}
// 业务code处理
return response;7
}, err => {
// Axios的error
if (err instanceof AxiosError) {
console.log('AxiosError', err);
if (err.config.fullLoading) {
service.close();
}
} else if (err instanceof Error) {
console.log('SyntaxError', err)
}
// 提示 非200的状态码 404 500
console.log(err, 'err');
Notification.error(err.message)
// 返回一个reject的Promise
return Promise.reject(err); // 错误处理就这么写
});
$语法
获取此组件的父组件
this.$parent
所有路由
// 获取所有路由信息
this.$router.options.routes
// 跳转到home
this.$router.push("/home");
当前路由
// 获取当前路由信息
this.$route.matched
获取ref标识的dom节点
```javascript
// 重置ref的表单验证
this.$refs['ref标识'].resetFields()
// 验证ref的表单数据
this.$refs['ref标识'].validate((valid) => {
if (valid) {
} else {
console.error(this.form)
}
});
返回给父组件
xxxxx.$emit();
elementui信息提示显示
```javascript
this.$message({ type: 'success', message: '获取数据成功' })
elementui信息提示询问
```javascript
this.$alert('你确定要删除吗?', '提示', {
confirmButtonText: '确定',
callback: () => {
}
})
echart使用
// 初始化
let myChart = this.$echarts.init(document.getElementById("main"));
// 注册
this.$echarts.registerMap("china", chinaJson);
nextTick的作用
强制渲染视图
返回的参数是一个异步的
// vue2写法
// 渲染前
this.$nextTick(()=>{
// 渲染后再执行
})
// vue3写法
import { nextTick } from 'vue'
// 渲染前
await nextTick()
// 渲染后再执行
data的使用
this.$data
el的使用
// dom节点
this.$el
// mounted下 子组件获取父组件的dom
this.$parent.$el
// mounted下 获取根节点的dom
this.$root.$el··
set的作用
强制渲染视图
this.$set(this.arr, '1', 'xxxx') // 让vue检测this.arr的1对象
children的作用
// 获取所有子组件
this.$children
把绑定的所有事件都绑定陈内部属性
@click="x1" @change="x2" // 外部绑定
v-on="$listeners" // 内部绑定
// 等于
@click="x1" @change="x2" // 外部绑定
@click="$emit('click', $event)" @change="$emit('change', $event)" // 内部绑定
把传进来的属性都应用成内部属性
title="xxx" :type="'rrrr'" // 外部绑定
v-bind="$attrs" // 内部绑定
// 等于
title="xxx" :type="'rrrr'" // 外部绑定
:title="$attr.title" :type="$attr.type" // 内部绑定
获取所有传进来的插槽
// 外部传了名叫btn插槽
this.$scopedSlots.btn
#btn="{t}" // 定义插槽接收值
btn({ t: '我是scopod' }) // 解构传值
// 等于
#btn="scoped" // 定义插槽接收值
btn('我是scopod') // 直接传值