导航


HTML

CSS

JavaScript

浏览器 & 网络

版本管理

框架

构建工具

TypeScript

性能优化

软实力

算法

UI、组件库

Node

业务技能

针对性攻坚

AI


.js 组件

Untitled

跟常规的 .vue 组件没什么区别,就是个对象,样式 import 引入就好

上边的 MyLogo 对象,其实就是平常 .vue 汇总的 export default 对象

为什么有 .vue 文件

为了将上边 index.jsindex.scss 整合到一个文件里(.vue)边去。

Untitled

Untitled

Untitled

全局组件

var app = new Vue({});

app.component('my-search', {
	data() {
  	return {
    	 placeholder: 'Please type the keyword...',
       keyword: ''
    }
  },
  template: `
		<div style="float: left;">
			<input
				type="text"
				:placeholder="placeholder"
				v-model="keyword" />
			<button @click="searchAction">Search</button>
		</div>
	`,
  methods: {
  	searchAction() {
    	console.log(this.keyword);
    }
  }
});

注册后就不用在各文件中注册了

全局组件放到 global 中去:

Untitled

导入:

Untitled

命名规范

使用组件,命名用 kebab-case

<tab-nav></tab-nav>

props 属性命名,推荐小驼峰

<!-- 父组件 -->
<tab-nav
	:navData="navData"
	:tabIndex="tabIndex"
/>

<!-- 子组件 -->
props: ['navData', 'tabIndex'],

🔥 props 可被 data、computed 使用

data() {
  return {
    tabIndex: this.tabData.initialIndex
  }
},
props: ['tabData'],
computed: {
  navData() {
    return this.tabData.data.map(item => { id: item.id, title: item.title });
  },
  pageData() {}
}

父子组件事件传递

子组件传递事件给父组件

🔥 父自定义事件、子 $emit 触发

子组件:

<div
 @click="changeIndex(myIndex)"><div>

export default {
  methods: {
    changeIndex(index) {
      this.$emit('changeIndex', index);
    }
  }
}

父组件:

<nav-item
	...
	@changeIndex="changIndex"
          />

export default {
	methods: {
    changIndex(index) {
      console.log(index);

			// 再向上传递
			this.$emit('changeIndex', index);
    }
  }
}

🔥 子组件使用 v-model 来更改父组件属性

data 配合 watch + $emit

子组件

<template>
  <div class="tab-search">
    <input
      type="text"
      v-model="value" />
  </div>
</template>

<script>
export default {
  name: 'tab-search',
  props: ['searchValue'],
	// 第一种方案:
  data() {
    return {
      value: this.searchValue
    }
  },
  watch: {
    value() {
      // 要不要 update: 都行,就是个普通自定义事件 例如写成 updateSearchValue 也行
      this.$emit('update:searchValue', this.value);
    }
  }
}
</script>

<style lang="scss">
  .tab-search {
    height: 50px;
    padding: 6px 10px;
    border-bottom: 1px solid #000;
    box-sizing: border-box;

    input {
      width: 100%;
      height: 38px;
      box-sizing: border-box;
    }
  }
</style>

父组件

<tab-search
	:searchValue="searchValue"
	@update:searchValue="updateSearchValue"
/>

export default {
	methods: {
		updateSearchValue(value) {
      this.tabData.data.some((item, index) => {
        if (item.title.includes(value) || item.content.includes(value)) {
          this.tabIndex = index;
          return true;
        }
      });
    }
	}
}

推荐 computed 的 setter、getter 配合 $emit

子组件

<template>
  <div class="tab-search">
    <input
      type="text"
      v-model="value" />
  </div>
</template>

<script>
export default {
  name: 'tab-search',
  props: ['searchValue'],
  // 第二种方案:
  computed: {
    value: {
      get() {
        // 直接返回父组件传递的值
        return this.searchValue
      },
      set(newValue) {
        // 这里不需要 this.value = newValue
        // 直接传递事件让外边改,value就自动改了
        this.$emit('update:searchValue', newValue);
      }
    }
  }
}
</script>

<style lang="scss">
  .tab-search {
    height: 50px;
    padding: 6px 10px;
    border-bottom: 1px solid #000;
    box-sizing: border-box;

    input {
      width: 100%;
      height: 38px;
      box-sizing: border-box;
    }
  }
</style>

父组件

<tab-search
	:searchValue="searchValue"
	@update:searchValue="updateSearchValue"
/>

export default {
	methods: {
		updateSearchValue(value) {
      this.tabData.data.some((item, index) => {
        if (item.title.includes(value) || item.content.includes(value)) {
          this.tabIndex = index;
          return true;
        }
      });
    }
	}
}

⚪️ 示例

子组件

<template>
  <div class="my-form">
    <div class="input-box">
      <input type="text"
        v-model="username"
        placeholder="Username"
      >
    </div>
    <div class="input-box">
      <input type="text"
        v-model="password"
        placeholder="Password"
      >
    </div>
  </div>
</template>

<script>
export default {
  name: 'my-form',
  props: ['myUsername', 'myPassword'],
  computed: {
    username: {
      get() {
        return this.myUsername; // 获取父组件传递的prop最新值
      },
      set(newVal) {
        this.$emit('update:myUsername', newVal); // 告知父组件得更新prop值了
      }
    },
    password: {
      get() {
        return this.myPassword;
      },
      set(newVal) {
        this.$emit('update:myPassword', newVal);
      }
    }
  }
}
</script>

<style>

</style>

父组件

<template>
  <div>
    <my-form
     :myUsername="myUsername"
     :myPassword="myPassword"
     @update:myUsername="updateMyUsername"
     @update:myPassword="updateMyPassword"
     ></my-form>
  </div>
</template>

<script>
import MyForm from './components/MyForm';

export default {
  name: "App",
  components: {
    MyForm
  },
  data() {
    return {
      modalShow: true,
      myUsername: '',
      myPassword: ''
    }
  },
  methods: {
    updateMyUsername(val) {
      this.myUsername = val;
    },
    updateMyPassword(val) {
      this.myPassword = val;
    }
  }
};
</script>

<style></style>

父组件如何触发子组件方法

通过给组件设置 ref

父组件 TodoHeader

<template>
  <div>
    <todo-input
      ...
      ref="todoInput"
    ></todo-input>
    ...
  </div>
</template>

<script>
...
export default {
  ...
    addTodo() {
      ...
      this.$refs.todoInput.reset();
    }
  }
}
</script>

子组件 TodoInput

<template>
  <input
    type="text"
    :placeholder="placeholder"
    v-model="content" />
</template>

<script>
export default {
  ...
  methods: {
    reset() {
      this.content = '';
    }
  }
}
</script>

is="vue:xxx"

浏览器中 table 不能嵌套 div、vue 组件,div 或者组件东西能出来,但是在 table 标签外边。

Untitled

要想解决此问题,可以显示的告诉vue我这是个组件:

Untitled

slot 简单使用

父组件使用:

<modal
	...
	>
  <my-form></my-form>
</modal>

子组件: