Vue router-link 对比a标签
Vue 中,为什么不能用 index 作为 key?
react/vue用index作为key可能会引发的问题
打印:
vue3和vue2对比
vue通信方式
全面总结 Vue 3.0 的新特性
vue计算属性和watch属性
computed和watch的区别是什么?
组件中 data 为什么是一个函数?
$nextTick 用法与原理
导航解析流程
keep-alive
Vue中的路由
服务端渲染
proxy api都可以做啥
vue3 diff和vue2 diff有啥区别
[vue常用的考点]
2022最新前端vue面试题
Vue学习之从入门到神经(两万字收藏篇)
vue面试题整理(2022-持续更新中…)
2022Vue经典面试题及答案汇总(持续更新)
2021年Vue最常见的面试题以及答案(面试必过)
【面试题】2021最新Vue面试题汇总
vue通信方式
浅析我们为什么要上Vue3?
vue3和vue2对比
------Vue2不好的地方
对TypeScript支持不友好(所有属性都放在了this对象上,难以推倒组件的数据类型)
大量的API挂载在Vue对象的原型上,难以实现TreeShaking。
架构层面对跨平台dom渲染开发支持不友好
CompositionAPI。受ReactHook启发
更方便的支持了 jsx
Vue 3 的 Template 支持多个根标签,Vue 2 不支持
对虚拟DOM进行了重写、对模板的编译进行了优化操作...
------Vue3 api
1.更强的性能,更好的 tree shaking
2. Composition API + setup
3. 更好地支持 TypeScript
------Vue3 性能方面提升
1. 编译阶段。对 diff 算法优化、静态提升等等
2. 响应式系统。Proxy()替代Object.defineProperty()监听对象。监听一个对象,不需要再深度遍历,Proxy()就可以劫持整个对象
3. 体积包减少。Compostion API 的写法,可以更好的进行 tree shaking,减少上下文没有引入的代码,减少打包后的文件体积
4. 新增片段特性。Vue 文件的<template>标签内,不再需要强制声明一个的<div>标签,节省额外的节点开销
v-cloak 防止抖动
@Prop() 后边的!后对进行非空断言
https://www.leevii.com/2018/10/what-does-the-exclamation-point-behind-the-ts-attribute-mean.html
v-model也可以和.lazy、.trim和.number这些修饰符一起使用
provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的属性还是可响应的。
打印打印打印打印
具名插槽和作用域插槽(就是把vue的子元素的属性给父使用)
// 爸爸
<YanSlotSon>
<template v-slot:header="context">
fffffff
{{context.aaa}}
</template>
</YanSlotSon>
// 儿子
<template>
<div>
<slot name="header" :aaa="'kkkkk'"></slot>
</div>
</template>
通信方式
Prop(常用)包含方法通过props传递
$emit (组件封装用的较多)
.sync语法糖 (较少)
$attrs & $listeners (组件封装用的较多)
provide & inject (高阶组件/组件库用的较多)
slot-scope & v-slot (vue@2.6.0+)新增
v-model 与 value 和 input一样
<YanSyncSon :value="parentinfo" @input="data => (this.parentinfo = data) "></YanSyncSon>
<YanSyncSon v-model="parentinfo"></YanSyncSon>
.sync语法糖
<YanSyncSon :value="parentinfo" @update:value="data => (this.parentinfo = data) "></YanSyncSon>
<YanSyncSon :value.sync="parentinfo"></YanSyncSon>
form组件
// 使用
<k-form :model="model" :rules="rules" ref="loginForm">
<k-form-item label="用户名" prop="username">
<k-input v-model="model.username" autocomplete="off" placeholder="输入用户名"></k-input>
</k-form-item>
<k-form-item label="确认密码" prop="password">
<k-input type="password" v-model="model.password" autocomplete="off"></k-input>
</k-form-item>
<k-form-item>
<button @click="submitForm('loginForm')">提交</button>
</k-form-item>
</k-form>
submitForm(form) {
this.$refs[form].validate(valid => {
const notice = this.$create(Notice, {
title: "社会你杨哥喊你来搬砖",
message: valid ? "请求登录!" : "校验失败!",
duration: 1000
});
notice.show();
});
}
// form 的
<template>
<div>
<slot></slot>
</div>
</template>
<script>
export default {
provide() {
return {
form: this
};
},
props: {
model: {
type: Object,
required: true
},
rules: {
type: Object
}
},
methods: {
validate(cb) {
const tasks = this.$children
.filter(item => item.prop)
.map(item => item.validate());
// 所有任务都通过才算校验通过
Promise.all(tasks)
.then(() => cb(true))
.catch(() => cb(false));
}
}
};
</script>
// form-item
<template>
<div>
<label v-if="label">{{label}}</label>
<slot></slot>
<p v-if="errorMessage">{{errorMessage}}</p>
</div>
</template>
<script>
import Schema from 'async-validator'
export default {
inject: ["form"],
props: {
label: {
type: String,
default: ""
},
prop: {
type: String
}
},
data() {
return {
errorMessage: ""
};
},
mounted() {
this.$on('validate', this.validate)
},
methods: {
validate() {
// 做校验
const value = this.form.model[this.prop]
const rules = this.form.rules[this.prop]
// npm i async-validator -S
const desc = {[this.prop]: rules};
const schema = new Schema(desc);
// return的是校验结果的Promise
return schema.validate({[this.prop]: value}, errors => {
if (errors) {
this.errorMessage = errors[0].message;
}else {
this.errorMessage = ''
}
})
}
},
};
</script>
// input
<template>
<div>
<input :value="value" @input="onInput" v-bind="$attrs">
</div>
</template>
<script>
export default {
inheritAttrs: false,
props: {
value: {
type: String,
default: ""
}
},
methods: {
onInput(e) {
this.$emit("input", e.target.value);
this.$parent.$emit('validate');
}
}
};
</script>
tree
<template>
<li>
<div @click="toggle">
{{model.title}}
<span v-if="isFolder">[{{open ? '-' : '+'}}]</span>
</div>
<ul v-show="open" v-if="isFolder">
<item class="item" v-for="model in model.children" :model="model" :key="model.title"></item>
</ul>
</li>
</template>
<script>
export default {
name: "Item",
props: {
model: {
type: Object,
required: true
}
},
弹窗组件的使用
const notice = this.$create(Notice, {
title: "社会你杨哥喊你来搬砖",
message: valid ? "请求登录!" : "校验失败!",
duration: 1000
});
export default function create(Component, props) {
// 先创建实例
const vm = new Vue({
render(h) {
// h就是createElement,它返回VNode
return h(Component, {props})
}
}).$mount();
// 手动挂载
document.body.appendChild(vm.$el);
// 销毁方法
const comp = vm.$children[0]; // 这个就是Notice组件
comp.remove = function() {
document.body.removeChild(vm.$el);
vm.$destroy();
}
return comp;
}
vuex
dispatch 对应 commit
Actions 对应 mutations
// 页面中使用store
computed: {
doneTodosCount () {
return this.$store.getters.doneTodosCount
}
}
// 使用kstore.js
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
export default new Vuex.Store({
state: { count: 0 },
mutations: {
increment(state, n = 1) {
state.count += n;
}
},
getters: {
score(state) {
return `共扔出:${state.count}`
}
},
actions: {
incrementAsync({ commit }) {
setTimeout(() => {
commit("increment", 2);
}, 1000);
}
}
});
// ======== kvuex
let Vue;
class Store {
constructor(options) {
this.state = new Vue({
data: options.state
});
this.mutations = options.mutations;
this.actions = options.actions;
options.getters && this.handleGetters(options.getters)
}
// 声明为箭头函数,why?
commit = (type, arg) => {
this.mutations[type](this.state, arg);
};
dispatch(type, arg) {
this.actions[type]({
commit: this.commit,
state: this.state
}, arg);
}
handleGetters(getters) {
this.getters = {};
// 遍历getters所有key
Object.keys(getters).forEach(key => {
// 为this.getters定义若干属性,这些属性是只读的
// $store.getters.score
Object.defineProperty(this.getters, key, {
get: () => {
return getters[key](this.state);
}
})
})
}
}
function install(_Vue) {
Vue = _Vue;
Vue.mixin({
beforeCreate() {
if (this.$options.store) {
Vue.prototype.$store = this.$options.store;
}
}
});
}
export default { Store, install };
krouter.js
import Home from "./views/Home";
import About from "./views/About";
import Vue from "vue";
class VueRouter {
constructor(options) {
this.$options = options;
this.routeMap = {};
// 路由响应式
this.app = new Vue({
data: {
current: "/"
}
});
}
init() {
this.bindEvents(); //监听url变化
this.createRouteMap(this.$options); //解析路由配置
this.initComponent(); // 实现两个组件
}
bindEvents() {
window.addEventListener("load", this.onHashChange.bind(this));
window.addEventListener("hashchange", this.onHashChange.bind(this));
}
onHashChange() {
this.app.current = window.location.hash.slice(1) || "/";
}
createRouteMap(options) {
options.routes.forEach(item => {
this.routeMap[item.path] = item.component;
});
}
initComponent() {
// router-link,router-view
// <router-link to="">fff</router-link>
Vue.component("router-link", {
props: { to: String },
render(h) {
// h(tag, data, children)
return h("a", { attrs: { href: "#" + this.to } }, [
this.$slots.default
]);
}
});
// <router-view></router-view>
Vue.component("router-view", {
render: h => {
console.log(this.routeMap[this.app.current]);
const comp = this.routeMap[this.app.current];
return h(comp);
}
});
}
}
VueRouter.install = function(Vue) {
// 混入
Vue.mixin({
beforeCreate() {
// this是Vue实例
if (this.$options.router) {
// 仅在根组件执行一次
Vue.prototype.$router = this.$options.router;
this.$options.router.init();
}
}
});
};
Vue.use(VueRouter);
export default new VueRouter({
routes: [{ path: "/", component: Home }, { path: "/about", component: About }]
});
导航解析流程
调用全局的 beforeEach 守卫。
在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)
在路由配置里调用 beforeEnter。
解析异步路由组件。
在被激活的组件里调用 beforeRouteEnter。
调用全局的 beforeResolve 守卫 (2.5+)。
导航被确认。
调用全局的 afterEach 钩子
v-pre
不会解析直接输出
常用的标签
v-for /v-bind(😃 /v-if/ v-show/ v-else-if /v-else/ v-model / v-on(@) / v-text / v-html / v-once /
v-prev / v-cloak
原理 2. 性能 3. 场景
computed和watch的区别是什么?
computed 一对多, 多次调用时,会把第一次调用的结果放入缓存,节约性能
定义的函数必须要写 return,不然会报错
调用的时候不能加括号,不然会报错
在computed中定义一个函数(看起来是一个函数,其实是一个属性)
watch 多对一 只监听,不会产生新的函数名,watch也可以渲染数据,但是和computed比较就比较复杂。
组件中 data 为什么是一个函数?
组件上的data是函数的情况下,组件每次调用data里面的数据,都是由data这个独有的函数返回过来的数据,
所以不会造成这个组件更改data的数据,另一个在使用这个数据的组件也会更改这个数据
$nextTick 用法与原理
在数据变化后要执行某个回调函数,而这个操作需要使用随数据改变而改变的DOM结构的时候, 这个操作都应该放进Vue.nextTick () 回调函数中。
在Vue 内部尝试对异步队列使用原生的promise.then 和 MessageChange,如果执行环境不支持, 会采用setTime(()=> {} , 0)代替。