为什么索引用key不行,因为如果比如排序vue发现key为0的dom节点不一样,会直接被干掉,然后重新渲染一个,如果用key为j的话,就会先去找缓存,
directives写在自己的组件里,就是自己组件的自定义方法,这个el就是特殊绑定的的dom,el中有一个方法contains,可以判断点击的dom,在不在绑定的dom范围内,这第二个参数bindings就是绑定的dom上的属性可以拿到,vnode.context就是当前实例的,
接下来我们看一下vue-lazy,更应该学习,她的设计模式和方式
/* eslint-disable no-mixed-spaces-and-tabs */
const loading = "http://localhost/images/1.gif";
//@ts-ignore
// eslint-disable-next-line no-undef
Vue.use(VueLazyload, {
preload: 1.3,
loading,
});
const getScrollParent = (el) => {
let parent = el.parentNode;
while (parent) {
// eslint-disable-next-line no-undef
if(/(scroll) | (auto) / .test(getComputedStyle(parent)["overfliow"])) {
return parent;
}
parent = parent.parentNode;
}
return parent;
}
const loadImageAsync = (src, resovle, reject) => {
// eslint-disable-next-line no-undef
let image = new Image();
image.src = src;
image.onload = resovle;
image.onerror = reject;
}
const Lazy = (Vue) => {
class ReactiveListener{
constructor({el, src, options, elRender}) {
this.el = el;
this.src = src;
this.options = options;
this.elRender = elRender;
this.state = {loading: false}
}
checkInView() {
let { top } = this.el.getBoundingClientRect();
// eslint-disable-next-line no-undef
return top < window.innerHeight * this.options.preload || 1.3;
}
load() {
// 用来加载这个图片 先加载loading 如果加载ok的话 显示正常图片
loadImageAsync(this.src, ()=>{
this.state.loading = true;
this.elRender(this, "finish");
}, ()=>{
this.elRender(this, "error");
});
}
}
return class LazyClass{
constructor(options) {
this.options = options;
this.bindHandler = false;
this.listenerQueue = [];
}
add(el, bindings) {
Vue.nextTick(()=> {
let scrollParent = getScrollParent(el);
if(scrollParent && !this.bindHandler) {
this.bindHandler = true;
scrollParent.addEventListener("scroll", this.handleLazyLoad.bind(this));
}
// bindings.value 可以拿到渲染后的属性值, bindings.expression 是拿到渲染前的值
const listener = new ReactiveListener({
el,
src: bindings.value,
options: this.options,
elRender: this.elRender.bind(this),
});
this.listenerQueue.push(listener);
this.handleLazyLoad();
})
}
// eslint-disable-next-line @typescript-eslint/no-empty-function
elRender(listener, state) {
let el = listener.el;
let src = "";
switch (state) {
case "loading":
src = listener.options.loading || "";
break;
case "error":
src = listener.options.error || "";
break;
default:
src = listener.src;
break;
}
el.setAttribute("src", src);
}
handleLazyLoad() {
// eslint-disable-next-line @typescript-eslint/no-empty-function
this.listenerQueue.forEach(listener => {
let catIn = listener.checkInView();
catIn && listener.load();
});
}
}
}
const VueLazyload = {
install(Vue, options) {
const LazyClass = Lazy(Vue);
const lazy = new LazyClass(options);
Vue.directive("lazy", {
bind: lazy.add.bind(lazy),
});
}
};
很多面试官会问,组件有什么作用,比如父组件有两个子组件,一个是a组件,一个是b组件,a组件数据变了的话不会导致父组件和b组件的更新,如果不分组件的话,整个页面都会更新
这样在vue-cli脚手架中其实是不支持template,默认是runtime-only,当然这是在main.js中进行的,如果想要支持template的写法,需要在vue.config.js中写,配置,runtimeComplier:true,就可以
这个$on相当于给Edit绑定一个input事件,下面是事件
这样触发
也可以这样触发,触发的都是@后面的名字
组件中为什么data必须是返回的函数?不能是对象?因为如果用ref进行拿到B实例,改变data的话,在其它组件拿到B实例,data也是改变后的
为什么传值不能用bus不好维护,一般不要用
看一看表单怎么封装的
<div>
<!-- el-form 表单 modal (数据源),rules (规则 ) vue是单向数据流 -->
<el-form :modal="ruleForm" :rules="rules" ref="ruleForm">
<!-- el-form-item 表示div 校验 label + 属性校验 -->
<!-- v-modal 纯的语法糖 相当于绑定一个input事件 还给了一个value -->
<el-form-item label="用户名" props="username">
<el-input v-modal="ruleForm.username"></el-input>
</el-form-item>
<el-form-item label="密码" props="password">
<el-input v-modal="ruleForm.password"></el-input>
</el-form-item>
<el-form-item>
<button @click="submitForm">提交表单</button>
</el-form-item>
</el-form>
</div>
</template>
<script>
import elForm from "./components/el-form";
import elInput from "./components/el-input";
import elFormItem from "./components/el-form-item"
export default {
components: {
"el-form": elForm,
"el-input": elInput,
"el-form-item": elFormItem,
},
data(){
return{
ruleForm: {
username: "",
password: "",
},
rules: {
username:[
{require:true, message:"请输入用户名"},
{min:3, max:5, message:"长度在3-5"}
],
password: [{require: true, message: "请输入密码"}],
}
}
},
methods: {
submitForm(formName) {
this.$refs["ruleForm"].validate(valid => {
if(valid){
} else {
return false;
}
});
}
}
}
</script>
<template>
<form>
<slot></slot>
</form>
</template>
<script>
export default {
name:'elForm',
provide(){ //这个跨层级传值的最好不要用 相当于给自己组件实例上给_provided加上暴露出去的
return{
elFormItem:this
}
},
props:{
rules:{
type:Object,
default:()=>({}),
},
modal:{
type:Object,
default:()=>({}),
}
},
methods:{
validate(){
}
}
}
</script>
<template>
<div>
<label v-if="label">{{label}}</label>
<slot></slot>
<span v-if="errorMessage">{{errorMessage}}</span>
</div>
</template>
<script>
export default {
name:"elFormItem",
inject:[elForm], // 去找父亲的 ._provided属性
data(){
return {errorMessage:null}
},
props:{
label:{
type:String
},
prop:{
type:String
}
},
mounted(){ //挂载顺序是先子后父
this.$on("validate",()=>{
})
},
methods:{
validate(){
let model = this.elForm.model[this.props]
let ruleValue = this.elForm.rules[this.props];
}
}
}
</script>
<template>
<input type="text" :value="aaa" @input="handleInput">
</template>
<script>
export default {
modal:{
// 可以重新定义v-modal解析出来的结果
prop:'aaa'
},
name:"elInput",
props:{
aaa:{
type:String
}
},
methods:{
handleInput(e){
this.$emit('input',e.target.value);
// this.$parent 这个表示是父组件 就算是子组件外面包了一层div,找的不是div这个dom,而是div外面的组件
// 但是如果想拿到外层那个div的dom的话就需要用 this.$el.parentNode this._uid是这个组件的唯一标识
// 这种方式不行,为什么呢,因为万一有的程序员,外面没包div包了一个自己封装的组件,那就找不到对应的el-form-item了
// this.$parent.$emit("validate");
// 怎么办就需要自己实现dispatch
this.$dispatch('elFormItem','validate');
}
}
}
//这是精确找到表单父组件的
Vue.prototype.$dispatch = function(componentName,eventName) {
let parent = this.$parent;
while(parent){
let name = parent.$options.name;
if(name === componentName){
break;
} else {
parent = parent.$parent;
}
}
if(parent){
if(eventName){
parent.$emit(eventName)
}
return parent;
}
}
//这是精确的找到表单子组件
Vue.prototype.$broadcast = function(componentName,eventName) {
let children = this.$children;
let arr = [];
function findChildren(children){
children.forEach(child => {
if(child.$options.name === componentName){
if(eventName){
child.$emit(eventName);
}else{
arr.push(child);
}
}
if(child.$children){
findChildren(children.$children);
}
});
}
findChildren(children);
return arr;
}
</script>
这个时候如果直接监控name的话,
vm.name.a = 1
这种改变是监控不了的
必须要
vm.name = {b:2}